A geometry explorer for special right triangles on a Cartesian
grid. Triangles are randomized each time you press “New Triangle” and
can be displayed in either their canonical (unrotated) or rotated orientation.
In Textbook mode, every side length is always a clean radical
expression — no ugly decimals, ever.
🎛️ Controls
- Type
-
Choose General Right, 30-60-90, 45-45-90,
Equilateral, or Random.
- Grid size n
-
Sets the grid to ±n units. Triangle dimensions scale
automatically to remain visible and span at least half the grid.
- Textbook problems only
-
When checked, all three side lengths are guaranteed to be exact
radical expressions (or integers). Hover any side label to see its
decimal approximation as a tooltip.
- Pythagorean triple chance
-
Visible only in Textbook + General Right mode. Sets the probability
(0–100 %) that the generated triangle will have an
integer hypotenuse (e.g. 3-4-5, 5-12-13, 9-12-15).
Default is 5 %. Takes effect on the next “New Triangle” press.
- New Triangle
-
Randomizes a new shape and a new rotation angle (0–359°).
- Show Original / Show Rotated
-
Toggles between the rotated display and the canonical upright
orientation — same triangle, no re-randomization.
- 📐 Annotate
-
Toggles side-length and angle labels drawn directly on the canvas,
close to the values they describe. Side labels appear at each side’s
midpoint, offset outward from the triangle interior. Angle labels appear
near each vertex, offset inward toward the centroid. All labels are
color-coded to match the vertex scheme:
red = A,
green = B,
blue = C.
In Textbook mode, side labels show exact radical form
(e.g. 3√5);
otherwise a two-decimal approximation is shown.
Click again to hide the labels.
- 💾 Save Image
-
Downloads the current canvas — including any active annotations
— as a PNG file.
△ Triangle Types & Side Ratios
The right angle (when present) is always at vertex
C (blue dot).
Sides: a = BC,
b = CA,
c = AB (hypotenuse).
| Type |
Angles |
Side ratios (short leg = s) |
| General Right |
90°, A°, B° |
a, b,
√a²+b²
|
| 30-60-90 |
30°, 60°, 90° |
s, s√3, 2s |
| 45-45-90 |
45°, 45°, 90° |
s, s, s√2 |
| Equilateral |
60°, 60°, 60° |
s, s, s |
√ The Radical Class
Radical.js represents an expression of the form
c · n√r
where c (coefficient), n (root index), and
r (radicand) are integers.
simplify()
-
Reduces to simple radical form via prime factorization.
For each prime pe in the radicand,
⌊e / n⌋ copies come
outside the radical (multiplied into the coefficient) and
e mod n copies remain inside.
Example: √12
= √4 · 3
= 2√3.
toHTML()
-
Renders typeset HTML using
.rad-vinculum (CSS overline
for the radical bar) and .rad-index (superscript root
index for cube roots and higher). When the radicand simplifies to 1,
it returns just the integer coefficient — no radical bar shown.
approx(decimals)
-
Decimal approximation as a fixed-point string, e.g.
"1.414" for √2.
Used for tooltip text in the app.
multiply(other)
-
Multiplies two same-root radicals and returns the result in simple form.
Useful for computing derived lengths algebraically
(e.g. 4√3
× √3 = 12).
simplifySteps()
-
Returns a human-readable array of strings walking through each
step of the simplification — designed for educational display.
The bridge into the app is distToRadical(d) in the sketch:
given a floating-point distance d, it checks whether
d² rounds to an integer (tolerance < 0.001).
If so, it returns new Radical(1, 2, round(d²)).simplify().
This is how a numeric side length becomes a typeset radical expression.
🧮 Textbook Mode: Under the Hood
The core invariant: if a side has length
d = k√m
(k a positive integer, m square-free), then
d² = k²m is
exactly an integer. The distToRadical bridge will
always identify it cleanly, and simplify() will always
produce the correct simple form.
- 45-45-90
-
Leg = k√m
for any square-free m ≤ 35.
leg² = k²m ✓,
hyp² = 2k²m ✓.
When m=2 the legs are radical and the hyp is an integer
(e.g. leg = 3√2
⇒ hyp = 6).
- 30-60-90
-
Short leg s = k√m
for square-free m ≤ 123.
All three sides have integer squares:
s² = k²m,
(s√3)² = 3k²m,
(2s)² = 4k²m.
- General Right
-
Each leg independently drawn from the k√m
pool. Because both leg² values are integers, hyp² = legX²
+ legY² is also an integer — all three sides always display cleanly.
The Pythagorean triple submode forces legX and legY to be integers
whose squares sum to a perfect square (integer hypotenuse).
- Rotation-safe bounds
-
After rotation, if any vertex exceeds the grid limit a uniform scale
factor is applied — which would destroy the integer-square
property and produce raw decimals. Each triangle type has a
per-type sMax or legMax derived from the farthest possible
vertex distance, guaranteeing the scale factor is always exactly 1.
🧑💻 Developer Notes
- Developer
- klp + GitHub Copilot
- Date
- May 2026
- Stack
-
SketchWaveJS · p5.js 1.6.0 · Bootstrap 5.3.2
- Key files
-
specTri1Sketch.js — all triangle logic, textbook
generation, and control wiring.
resources/Radical.js — reusable radical-expression
class (coeff · √radicand),
used here for side-length display but applicable anywhere exact
radical arithmetic is needed.
styles/designerStyles.css — defines
.rad-vinculum and .rad-index for radical
HTML rendering.
- Design notes
-
The “integer-squared” invariant is the backbone of textbook
mode: generating side lengths of the form k√m
(m square-free) guarantees that floating-point distances are always
recognizable as clean radicals, even after trigonometric rotation is applied.
Rotation-safe sMax/legMax values were derived analytically by computing
the maximum distance each triangle type’s farthest vertex can reach
from the origin.