Kite Digital Twin — 9 m² LEI, two-line flexible tether anchor at origin · wind along +x · z up
AUTO ∞ PAUSED LUFF LINES SLACK
←→/A D steer  ·  ↑↓/W S depower​/power  ·  Q/E line out/in  ·  F autopilot fig-8  ·  R reset  ·  Space pause  ·  drag orbit / wheel zoom

Wind window (pilot's view, looking downwind)

Telemetry

Bar tension — anchor segments, L + R
Apparent wind
Kite ground speed
Elevation
Azimuth
C_L / C_D (E)
Wind @ kite alt
Lines
Steer u / power
FPS
Altitude

Parameters

Physics model & equations ↓

Physics model

Everything below is exactly what the code integrates — symbols map one-to-one onto the PARAMS block at the top of the file. Frame: anchor at the origin, +x downwind, +y crosswind (right-handed), +z up; ground at z = 0.

1 · Wind field

Logarithmic atmospheric boundary layer, blowing along +x, with an optional multiplicative gust factor built from three slow sines:

W(z) = Wref · ln( max(z, z0) / z0 ) / ln( zref / z0 )
Wgust(z,t) = W(z) · [ 1 + Σi ai sin( 2πt/Ti + φi ) ]   zref = 10 m, z0 = 0.03 m, Σai ≈ 0.115, Ti = 4.3 / 7.9 / 13.7 s

2 · Kite state and apparent wind

The kite is a point mass m at p with velocity v, plus one extra state: the heading h, a unit vector kept in the tangent plane of the tether sphere. With er = p/|p| the radial direction, each substep re-projects her and renormalizes (a cheap parallel transport). The apparent wind includes the kite's own velocity — this is what makes a downloop load up and an upstroke go light:

va = W(pz) v

3 · Drag polar and depower

Sheeting (power ∈ [0,1]) moves CL and CD together along a quadratic polar, so depowering lowers both lift and glide ratio E = CL/CD and the kite drifts toward the window edge:

CL0 = CL,min + ( CL,max − CL,min ) · power
CD = CD,min + kind CL02,    kind = 1 / ( 4 Emax2 CD,min )

A luff (front-stall) factor kills lift when the apparent wind acquires an inward radial component — i.e. when the kite is pushed from outside the window shell, as when it overflies zenith. With ξ = (va·er)/|va|:

CL = CL0 · smoothstep( clamp( 1 + ξ/ξfade, 0, 1 ) )   ξfade = luff_fade = 0.25; full lift for ξ ≥ 0, none below ξ = −0.25

4 · Aero frame, roll steering, forces

The spanwise axis and lift direction are built from heading and apparent wind; the identity va×(h×va) = h|va|2va(va·h) means is the normalized projection of the heading perpendicular to the flow — the kite pulls toward where its nose points. Steering input u rolls the lift vector about the flow axis (Rodrigues rotation):

= h × va / |·| ,   = va × / |·| ,   roll = R( a, φ ) ,   φ = kroll u

Forces on the kite particle (q = ½ρ|va|2A is the dynamic pressure times area):

Flift = q CL roll ,   Fdrag = q CD a ,   Fgrav = −m g
Fside = ½ρ A Cside |va| ( va·st ) st   dihedral side force: the LEI arc resists spanwise flow — linear sideslip damping that stills pendulum swing

The structural span st is the orthonormalized roll×h frame axis, so it banks with steering; the two wingtips (and line attachments) ride at p ± d·st with d = tip_offset·√(A/9).

5 · Heading: the turn-rate law

The single most important line in the file. Commanded turn rate scales with apparent wind speed — a parked kite barely answers the bar, a kite ripping across the window whips around. Two passive yaw torques share one geometric factor: gravity noses the kite toward tangential "down" (tg, the projection of − onto the tangent plane, |tg| = sin θpolar, vanishing at zenith), while nose-up trim yaws it toward zenith — what makes a released kite climb back to 12 o'clock:

ψ̇ = gk |va| u  +  [ kgrav g / max(|va|, va,min)  −  ktrim |va| ] · det[ er, h, tg ]
va* = √( kgrav g / ktrim ) ≈ 6.3 m/s   crossover: above va* the nose self-rights toward zenith; below it the kite noses over and falls — the light-wind stall

Integration: h is rotated about er by ψ̇·h each substep (h the substep length), then re-projected into the new tangent plane. u > 0 is the pilot's right; the roll and the turn law share the sign so the trajectory chases the nose.

6 · Tether: two XPBD lines, tension-only

Each side is a chain of N = 16 particles from its bar end (anchor, inverse mass 0, at y = ±0.25 m) to its wingtip; the wingtip point itself belongs to the kite, so the last segment's correction acts on the kite particle. Node mass = (line_mass/2)/N. Per rendered frame: 10 substeps of length h; per substep — apply forces, predict positions semi-implicitly, run 8 Gauss–Seidel passes over all segment constraints (alternating line order to avoid L/R solver bias), project ground contact, then recover velocities from the position delta:

vv + h w F ,   pp + h v   →   constraints   →   v = ( ppprev ) / h

The segment constraint is an inequality — it only acts when stretched. This is what produces slack line, sag, and the snap on re-tension:

C = |pipj| − Lseg ,   enforced only if C > 0
Δλ = ( −C − α̃λ ) / ( wi + wj + α̃ ) ,    α̃ = α / h2   α = 10−8 m/N — near-inextensible without being brittle
Δpi = +wi Δλ ,   Δpj = −wj Δλ ,   = ( pipj ) / |·|

The accumulated multiplier of each anchor-most segment gives the physical line force, the number a kiter recognizes at the bar (shown summed, and split L/R):

Fline = |λ0| / h2   [N]; displayed / 9.80665 as kgf. "Slack" when FL+FR < slack_tension

7 · Line aerodynamic drag

Per segment, only the component of relative wind normal to the segment matters (tangential drag on a 2 mm line is negligible); the force is split between the two end nodes. With the segment tangent and vrel = W(zmid)seg:

vn = vrel − ( vrel· ) ,   Fd = ½ ρ Cd,line dline Lseg |vn| vn

8 · Contact, damping, safeguards

Ground is a hard projection: line nodes to z ≥ 0.03 m, the kite centre to z ≥ 0.25 m (wingtip clearance); on contact, downward velocity is zeroed and horizontal velocity decays 10% per substep (friction). A global linear damping of 0.04 s−1 removes residual high-frequency jitter, speeds are hard-clamped at 120 m/s, and any non-finite state triggers an automatic reset.

9 · Figure-eight autopilot

Heading pursuit — the same law a human uses: point the nose at the waypoint. The target sits at azimuth ±45°, elevation 40°, flipping side whenever the kite's azimuth passes ±30°. With wp the waypoint direction projected on the tangent plane:

u = clamp( kap · ∠( hwp ), −1, 1 )   kap = ap_gain = 2.5 per rad; u is then slewed at steer_rate like a human hand

10 · Fitting this to a real kite

Three parameters dominate observable behaviour: E_max (crosswind speed and pull), g_k (loop period per bar input), and kite_mass (effective, incl. added mass — light-wind sink and pendulum period). Fit against footage: loop period at known wind, maximum stable park elevation, sink rate below flying wind. window.SIM exposes setInput(u, power), fastForward(seconds) (headless physics, no rendering) and telemetry() for automated parameter sweeps.

SymbolPARAMSDefaultRole
ρ, grho, g1.225, 9.81air density, gravity
A, mkite_area, kite_mass9 m², 3 kgprojected area, effective mass
CL,max/min, CD,min, EmaxC_L_max/min, C_D_min, E_max0.9 / 0.15, 0.15, 6.0drag polar
gk, krollg_k, k_roll0.10 rad/m, 0.6 radturn-rate gain, lift roll
kgrav, ktrim, Csidek_grav_turn, k_trim, C_side0.20, 0.05, 0.6passive yaw & sideslip stability
L, dline, Nline_length, line_diameter, N24 m, 2 mm, 16tether discretization (per side)
α, S, itcompliance, substeps, iterations10−8, 10, 8XPBD solver