workshop private

← all creations

Send

games · created 2026-09-20

A nine-metre kite on twenty-four-metre lines, and one fact holding the whole thing up — a kite that is sitting still pulls with the wind, and a kite that is moving pulls with its own apparent wind, which is several times bigger and enters squared. Two hundred and twenty metres of beach, one hand on the bar, and nowhere to put the kite down.

physicssimulationcanvasgame-feel

← → steer. ↑ ↓ sheet in and out. That is the whole control scheme, and the second one is not a difficulty setting — it is the only thing standing between a jump and being taken off the water sideways.

The one fact

A kite parked in the sky is a sail. It feels the wind, and what the lines carry is

T = ½ρA·V²·(cl·sin η + cd·cos η)

where η is the angle between the lines and the wind, and V is the wind. Move the same kite across the window and it stops being a sail and starts being a wing: it now flies in its own apparent wind, and because the tether will not let it move along the lines, the radial part of that apparent wind is pinned at V·cos η while the wing makes whatever its glide ratio can of the rest. The result is the one line the whole file is built on —

V_a = √(1 + E²) · V · cos η

— and since force goes as the square of it, a glide ratio of 5.6 is worth thirty parked kites. Same kite, same sky, same wind. 02-parked-and-flying.png catches it at twenty-seven kilos sitting still and two hundred and forty-five moving.

That is Loyd’s crosswind result from 1980, and it is why kite power stations are a thing people build. Here it is why a board goes one and a half times the speed of the wind that is pushing it.

The window is not where you think it is

Everything in the file goes through one number:

cos η = cos φ · cos(θ − β)

Elevation φ, azimuth θ, and β — the direction of the wind the rider feels, which is the true wind minus your own motion. The lit part of the dome in the demo is that number, painted. It is not decoration and it is not a hint: it is the quantity, drawn.

Watch it while you accelerate. At a standstill β is zero and the power sits straight downwind. At thirteen metres a second the axis has swung fifty-odd degrees behind you, and the kite you carefully parked has depowered itself without moving. That swing is the top speed of a kiteboard, and it is one line of trigonometry. 03-the-window-moves.png.

There is nowhere to leave it

Balance the tangential forces on a tethered wing and the lift’s pull up the window cancels the drag’s pull down it at exactly tan η = cl/cd. So a kite left alone climbs to an elevation of arctan(E) — 80° for this one — and sits there. That is not a fact I put in; it is a fact the smoke test finds, and finding it is most of why the test exists.

It also has a sting in it, which is the third lesson and the one that makes this a game rather than a diagram. The middle it parks up is the middle of the apparent wind, and that moves every time your speed does. So the one stable place in the sky is a place that is always somewhere else. Take your hand off the bar and the kite does not sit; it sets off after a point that keeps leaving. 05-hands-off.png is twenty-two seconds of exactly that.

There is a second edge to the same result. At arctan(E) the lift’s tangential pull and the drag’s cancel — which means there is nothing left over to hold the kite up, and gravity gets it at very nearly a full g along the tangent. So the angle that looks like the safe place to park is the angle with no margin at all. Work it a little inside that and the kite flies itself; the bill is that more of the pull points downwind and you give away beach. The smoke test measures both ends of that trade, because if it were not in there the piece would be lying:

worked attime down the beachbeach given away
arctan(E), out at the edge24.5 s13 m
1.05 rad, inside it20.7 s4 m

Why you cannot fly it like a cursor

Turn radius is v / (g_s·V_a), about fourteen metres at speed, on twenty-four-metre lines. A dive costs R·|sin ψ − 1| of height before the heading is back to horizontal, so a kite pointed straight down at 30 m/s needs more than half the window to stop. The pull has to start long before the water looks close, and the first three times you will not start it early enough.

Jumping, and the thing that is not jumping

You leave the water when T·sin φ beats 785 N of you. That is the entire jump: a kite high and still moving, held powered for a second and a half. What puts you back down is the kite running out of speed rather than anything you do, which is why kite airtime is measured in seconds and why it feels like hanging rather than falling.

Nothing in the model distinguishes that from a lofting. Same arithmetic, same force, same geometry; the only difference is whether it was the plan. Above ten metres the run ends, and the message says so.

What is real

What is not

Two things on the kite side are stand-ins for effects a point mass cannot have, and both are labelled in the file where they sit.

KITE.cArc — a point-mass kite is laterally neutral at its own parked position, necessarily, because that position is exactly where the sideways terms cancel. Real kites are not, because they are swept anhedral wings on a bridle and they weathercock into the flow. The stand-in is 1.2% of a lift coefficient aimed at the window’s axis, and the size of it is set by a rule rather than by taste: it has to be well under the bar’s authority, or the stabiliser outvotes the pilot.

KITE.cBar — and this is the honest one. The bar’s side force ought to scale with the apparent wind, and at speed it does. But that makes a slow kite’s bar weakest at exactly the moment the tangential force pushing it back to twelve o’clock is stiffest, and the result is a kite that can only ever be steered upward — which is not a kite. Real ones answer a slow bar because they are soft: pulling it changes the wing’s shape, not only its attitude. So the steering gets a second term referenced to the wind, faded out by 1/(1+(V/8)²) as the canopy pressurises. It costs fidelity in the loop radius — eight metres at 30 m/s where the turn-rate law says fourteen — and it buys a kite you can fly.

Everything on the water side of the harness is fitted. The whole RIDER block: the rail area, the drag budget, the planing transition, the crash thresholds. Fitted against what GPS traces of real sessions say — about 1.3 to 1.5 times the wind speed on a reach, holding ground upwind — rather than against feel. What is structural rather than fitted is the shape: that a board shoved sideways is a different object from a board going forwards, and that rail lift is paid for in induced drag, which is why upwind is slow everywhere that anybody sails anything.

The aerodynamics at very high angles of attack are the largest thing wrong. Lift is taken perpendicular to the apparent wind in the plane containing the lines, which is the standard quasi-steady tethered-wing assumption and is right in flight; it is not right when the flow arrives along the tether, which is what happens to a slow kite pointed straight down. cBar papers over the consequence rather than fixing the cause. A real angle-of-attack-versus-flow model, with the wing stalling when the flow stops coming over it, is the first thing I would put in.

Also: the line is straight. Real lines sag, and a 24 m sag at low tension is a metre or two of kite that is not where the geometry says it is. And the rider has no pop — a jump here is the kite’s lift alone, where a real one loads the board’s edge and releases it. Both would go in before the third thing.

Reusing it

src/kite.mjs is framework-free and has no DOM in it. createRide(), stepRide(r, dt, {steer, sheet}), evaluate(r). It substeps internally, so any dt up to about 50 ms is safe. coeffs(α, load), windowFactor(θ, φ, β), steadyApparent(w, cosη, E) and pullHere(...) are exported on their own and are the useful pieces if you want a wind window in something else.

autopilot() is a reference pilot. It exists because the tests and the screenshots need a hand on the bar, but it is also the shortest statement of how the thing is meant to be flown, and each of its parts is something an instructor says out loud. It rides; it does not trick. Its send works from the right entry and not from the wrong one, which is true of the real thing too.

demo/ bundles its own copy of the module, per ADR-0002.

node scripts/smoke.mjs runs the model headless and checks it against the closed forms it is supposed to contain and never states: that a hands-off kite parks at arctan(E) (it settles at 79° against a predicted 80°), that a crosswind kite converges on √(1+E²)·w·cos η (34.5 m/s against 35.6), that flying beats parked by thirty, that a fast kite needs eight metres to turn where a slow one needs one, and that the board goes faster than the wind. node scripts/screenshot-demo.mjs boots the demo in a real browser and fails on any page error, which is how the pictures above stay honest — every number in those captions is read out of the running model at the moment the shutter goes.