There is no steering wheel. The arrow keys roll the skis onto edge and that is the entire input, because that is the entire input a carving skier has. What comes out the other side is an arc you did not choose, at a speed that decides whether the arc exists at all.
Two lines, and everything else is a consequence
One. A ski has a waist narrower than its tip and tail. Tip it to edge angle
φ and press until the edge touches the snow along its whole length, and the
edge is now a circular arc of radius
R = R_sc · cos φ
R_sc is stamped on the ski. At 0° you go straight; at 60° a 17 m ski cuts
8.5 m. You are not turning the ski. You are picking a cosine.
Two. In a steady turn the resultant of gravity and the centripetal
requirement runs down the skier’s body, at angle λ_req from the snow normal
with tan λ_req = a_lat/g_n. The edge holds only while that resultant falls
inside the base — the platform-angle rule every ski coach teaches with a
hand and a forearm. So:
carve holds ⟺ φ ≥ λ_req , a_lat = v²/R
Substitute the first into the second and the edge angle cancels out of the right-hand side entirely:
sin φ ≥ v² / (g_n · R_sc) ≡ q
That is the whole creation. The right-hand side has no edge angle in it —
it is speed and a number stamped on the ski. So the shallowest edge that will
carve is fixed before you tip anything, it climbs with v², and the moment
q reaches 1 there is no angle at all:
v_max = √(g_n · R_sc)
44 km/h on a 17 m ski. Faster than that, on that ski, on that pitch, nothing carves — and essentially everyone on a piste spends most of their day above it. (The relation goes by the ideal-carving equation in the skiing literature; Jentschura & Fahrbach derived it in 2004. I did not invent it, I just made it a control scheme.)
The window, which is the thing you actually watch
q sets a floor on edge angle. The hip sets a ceiling — every degree of edge
past the lean the turn demands has to come out of angulation, and a hip runs
out somewhere around 25°. Between those two is a band of edge angles that
carve, and the demo draws it as a lit arc on the dial. The floor climbs with
v²; the band narrows; at v_max it has no width left.
node scripts/measure.mjs prints this, on a 17 m ski over hardpack:
| speed | carve load q | φ_min = asin q | φ_max (hip limit) | window | R at φ_min |
|---|---|---|---|---|---|
| 14.4 km/h | 0.099 | 5.7° | 31.7° | 26.0° | 16.9 m |
| 21.6 km/h | 0.224 | 12.9° | 41.7° | 28.7° | 16.6 m |
| 28.8 km/h | 0.397 | 23.4° | 65.6° | 42.1° | 15.6 m |
| 32.4 km/h | 0.503 | 30.2° | 65.6° | 35.4° | 14.7 m |
| 36.0 km/h | 0.621 | 38.4° | 65.6° | 27.2° | 13.3 m |
| 39.6 km/h | 0.751 | 48.7° | 65.6° | 16.8° | 11.2 m |
| 41.4 km/h | 0.821 | 55.2° | 65.6° | 10.3° | 9.7 m |
| 43.2 km/h | 0.894 | 63.4° | 65.6° | 2.1° | 7.6 m |
| 45.0 km/h | 0.970 | 76.0° | — | shut | 4.1 m |
Nothing in the demo closes that window. Math.asin closes it.
And there are two ways to be outside it, which is the part that makes it a mechanic rather than a readout:
- Below the floor — not enough edge for the speed. The arc asks for more lateral force than a shallow platform can stand under, the edge lets go, and the ski skids. Survivable, slow, and the track behind you turns from two thin grooves into a smear. This is the failure the controls invite, because backing off the edge is what anybody does when a turn gets frightening.
- Above the ceiling — more edge than the speed will hold up. The lean the turn demands is nowhere near the angle the ski is at, the hip tries to cover the difference, and runs out. That one ends the run.
Each ski is one number, and the number is a trade
| ski | R_sc | arc at 60° edge | ceiling |
|---|---|---|---|
| slalom | 12.5 m | 6.3 m | 37.4 km/h |
| all-mountain | 17 m | 8.5 m | 43.6 km/h |
| giant slalom | 30 m | 15.0 m | 57.9 km/h |
| downhill | 45 m | 22.5 m | 70.9 km/h |
Both columns come off the same R_sc, in opposite directions, which is why a
ski cannot be good at both and why the gates in the demo are a real question
rather than a scoreboard. Drive the same autopilot — four lines, solve for the
arc through the next gate, ask the sidecut for the edge angle that cuts it,
clamp into whatever window is open — down 600 m of 19 m gates:
| ski | gates cleared | missed | carved | top speed | ceiling | ended |
|---|---|---|---|---|---|---|
| SL 12.5 m | 12 | 19 | 76.8% | 37.6 km/h | 37.4 km/h | 128 s |
| all-mtn 17 m | 10 | 21 | 71.3% | 43.7 km/h | 43.6 km/h | 78 s |
| GS 30 m | 1 | 0 | 90.2% | 36.1 km/h | 57.9 km/h | caught an edge at 22 m |
| DH 45 m | 0 | 0 | 97.1% | 27.5 km/h | 70.9 km/h | caught an edge at 6 m |
The long skis carve beautifully and go straight into the first flag. The autopilot asks for a 13 m arc, a 45 m ski needs 73° of edge to cut one, the hip cannot find 73° at 27 km/h, and that is the end of that. Note the two “top speed” columns next to the two ceilings: a ski driven by something trying to carve ends up pinned at its own ceiling and nowhere near anybody else’s.
Carving is its own speed limit, which I did not expect to be so tidy
Take the braking away entirely — autopilot holds the shallowest carving edge, flips sides every two seconds, never once tries to slow down — and let the pitch push.
| ski | ceiling | 10° pitch | 15° | 20° | 25° |
|---|---|---|---|---|---|
| SL 12.5 m | 37.4 km/h | 26.2 | 35.3 | 36.3 | 39.2* |
| all-mtn 17 m | 43.6 km/h | 21.2 | 39.5 | 41.9 | 46.0* |
| GS 30 m | 57.9 km/h | 38.9 | 36.5 | 51.0 | 61.2* |
| DH 45 m | 70.9 km/h | 52.5 | 43.1 | 46.0 | 58.8 |
* = it got there with the window already shut.
Up to about 20° of pitch each ski parks near its own ceiling and stays there,
and the hill has very little to say about it: the arcs tighten as q rises,
tighter arcs cost more speed, and the thing self-limits. At 25° the pitch wins
on everything but the downhill ski, and winning means arriving somewhere the
window is closed — which is a precise way of saying this run is a skid now,
and a fair description of what steep groomers actually are.
The middle rows wobble (the GS ski does better at 10° than at 15°). That is the two-second flip interacting with arc length, not physics; the autopilot is a measuring stick, not a skier.
The dynamic ceiling is lower than the static one, by a consistent 13%
v_max is a statics result — it assumes you are already at that speed in a
steady turn. Enter a turn at v on the best edge the window allows and hold it
half a second, and gravity is adding speed the whole time, so the window closes
underneath the ski:
| ski | ceiling | highest entry speed that held 0.5 s | of ceiling |
|---|---|---|---|
| SL 12.5 m | 37.4 km/h | 31.9 km/h | 85% |
| all-mtn 17 m | 43.6 km/h | 37.3 km/h | 85% |
| GS 30 m | 57.9 km/h | 50.4 km/h | 87% |
| DH 45 m | 70.9 km/h | 62.3 km/h | 88% |
The ratio barely moves across a 3.6× range of sidecut, which is the tell that
it is the same v² showing up in q as in the acceleration, and not four
coincidences.
Snow moves exactly one number
The surface toggle changes a_max, the lateral acceleration the snow will key
an edge into, and nothing else. Everything downstream falls out:
| surface | a_max | useful edge stops at | ceiling on 17 m | ceiling on 30 m |
|---|---|---|---|---|
| ice | 0.80 g | 38.7° | 36.1 km/h | 48.0 km/h |
| hardpack | 2.20 g | 65.6° | 43.6 km/h | 57.9 km/h |
| soft | 1.25 g | 51.3° | 40.4 km/h | 53.6 km/h |
On ice at 35 km/h the window is 37°–39°. Two degrees. The dial in
media/06-ice.png is the clearest thing in the whole creation: the lit band is
a sliver, the needle is just past it, and the ski is gone. Nobody skis badly on
ice. The window closes.
The instinct, priced
Same ski, same 30 seconds, three policies:
- Back off the edge when it gets fast (8° under
φ_min) → 0% carved, 30 s skidding, tops out at 18 km/h, 40 m of hill covered. - Hold the shallowest edge that carves → 76.5% carved, 5.1 s skidding, 41.6 km/h, 308 m.
- Bury it — full edge, always → 96.5% carved, 0 s skidding, and it catches an edge at 25 km/h.
The first one is interesting because it is not a failure: skidding is a brake, and a brake that works. It is how everybody gets down a slope that is steeper than their ski’s ceiling, and the honest reading of the first row is not “played badly” but “chose 18 km/h.”
What is physics here and what isn’t
- Derived:
R = R_sc·cos φ;tan λ_req = a_lat/g_n; the platform-angle criterion;sin φ ≥ qandv_maxfalling out of them; the window’s two edges; every ceiling in every table above. - A modelling choice:
a_maxper surface. A carving edge sits in a groove it cut itself, so its lateral capacity is mechanical keying rather than Coulomb friction, and it is far above any sliding μ you could measure — but the three numbers are picked so thatatan(a_max)lands where coaching literature puts each surface’s useful edge limit, not measured off snow. Likewise the 25° hip, the 170°/s roll rate, and the skid friction. - A known omission, and it matters: a loaded ski bends, so a real carved
arc is tighter than
R_sc·cos φ— Howe’s correction, and the reason race skis are built around their flex as much as their sidecut. Tighter R means morea_latat the same speed, which means more lean, which means the real ceiling is below the one computed here, not above. Every number in this file is an optimistic bound. - A game, not a claim: the gates, the 19 m spacing, the camera, and the decision that catching an edge ends the run rather than costing you a second.
Reuse
src/sidecut.mjs is framework-free and has no canvas in it:
carveLoad(v, R_sc),minEdge,maxEdge,carveWindow,carveCeiling— the closed forms, usable on their own as a HUD for anything with a lean angle.edgeState(v, φ, ski, surface)— one instant:radius,demand,supply,slip(0 = pure carve, 1 = pure skid),lean,angulation,washing,overAngulated.createRun(opts)—step(dt, input)withinput ∈ [-1, 1]being which way you are rolling the skis, plusedge(),window(),ceiling().gateAt,clearedGate,autoSteer— the course layer, kept separate because it is the only part that is a game.
Integration is a fixed 1/240 s substep; the demo subdivides whatever the frame
gives it. The lean is not integrated as a pendulum — it is solved from balance
each step, which is right for a steady carve and wrong during the half-second
of a transition, where a real skier is falling across the skis. Porting note:
the whole of step() is scalar arithmetic with no allocations, so it drops
straight into a tick method.
The demo is demo/index.html with its own copy of the module (ADR-0002),
keyboard and drag both, and a window.__demo hook the screenshot rig drives.
node scripts/screenshot-demo.mjs regenerates the thumb and all seven media
shots and doubles as the smoke test — it rolls the ski with a real arrow key in
a real browser and asserts the edge angle and the arc that come back.





