I. Bicycle Aerodynamics — Why Speed Costs So Much Power
🚴Bicycle Aerodynamics — CdA, Power Demand & Drafting
Adjust speed, rider position, and altitude; see aerodynamic, rolling, and climbing power split live. Enable drafting and set the gap to a leading rider.
Cycling is a battle against air. At 40 km/h on flat ground, about 90% of a rider’s power output goes to pushing air out of the way. Rolling resistance and drivetrain friction share the remaining 10%. This is why professional riders obsess over body position, helmet shape, and sock height — tiny reductions in aerodynamic drag area translate directly into seconds saved per kilometre.
The aerodynamic drag force on a rider is proportional to the square of velocity and to a single composite parameter CₜA — the product of the drag coefficient Cₜ (dimensionless shape factor) and the frontal area A (in m²). Power, being force times velocity, then scales as the cube of speed. This cubic relationship is the central fact of cycling aerodynamics: doubling speed requires eight times the aerodynamic power.
F_drag = 0.5 * rho * v^2 * CdA
P_aero = F_drag * v = 0.5 * rho * v^3 * CdA
P_roll = Crr * m * g * cos(theta) * v (rolling resistance)
P_climb = m * g * sin(theta) * v (gravitational)
P_total = P_aero + P_roll + P_climb
Typical CdA values (rider + bike):
Upright commuting : 0.50 -- 0.60 m^2
Road drops position: 0.30 -- 0.38 m^2
TT tuck : 0.18 -- 0.24 m^2
Hour record : ~0.18 m^2
Air density: rho = 1.225 * exp(-altitude / 8500) (kg/m^3)
The simulation includes altitude adjustment, since air density drops exponentially with height — at 2000 m (typical of a mountain stage) the air is about 21% thinner than at sea level, reducing aerodynamic drag by the same fraction and partly explaining why records are often set at altitude.
Drafting: hiding in a slipstream
Following closely behind another rider places you in their wake — a region of turbulent, low-pressure air that partially shelters you from the incoming flow. The leading rider does most of the aerodynamic work; the follower saves energy. Wind-tunnel studies show that at a 2 m gap behind a single leader, a follower’s effective CdA is reduced by about 27%. In a peloton of 50 riders, riders buried in the middle may save 40% or more of their aerodynamic power.
CdA_follower(gap) ~ CdA_solo * (1 - 0.27 * exp(-gap / 3.5))
gap : distance behind leader (m)
At gap = 0 (touching wheels): ~35% reduction
At gap = 5 m : ~10% reduction
At gap = 10 m : ~4% reduction (negligible)
The simulation lets you position up to four riders in a line and watch the power requirement of each adjust in real time, illustrating why team tactics in cycling are so important: domestiques sacrifice their own race by leading to shelter the team leader, who arrives at the final climb with fresher legs.
II. FABRIK — Inverse Kinematics Without Trigonometry
🦾Inverse Kinematics — FABRIK Algorithm & Joint Constraints
Drag the target; watch a chain of 2–12 rigid links resolve in real time. Toggle joint angle limits and obstacle-avoidance. Shows convergence iterations per frame.
Inverse kinematics (IK) is the problem of finding joint angles that place a robotic arm’s end-effector at a desired position. The “inverse” is what makes it hard: going from joint angles to end-effector position (forward kinematics) is simple composition of rotations. Going the other way — from a target position to joint angles — has multiple solutions or no solution, and naively requires solving a nonlinear system of equations.
The classical approach uses the Jacobian matrix to linearise the problem and solves it iteratively with damped least-squares. This works but requires matrix inversion, which is expensive for many joints. FABRIK (Forward And Backward Reaching Inverse Kinematics), introduced by Aristidou and Lasenby in 2011, sidesteps all of that with a beautifully simple geometric observation: you don’t need to compute angles at all — just move the joints.
The algorithm
Label the joint positions p₀ (root) through pₙ (end-effector), with link lengths d₁ through dₙ. The forward pass pulls the chain toward the target; the backward pass re-anchors it to the root.
Forward pass (pull end-effector to target):
p[n] = target
for i = n-1 downto 0:
r = |p[i] - p[i+1]| (distance between adjacent joints)
lambda = d[i+1] / r
p[i] = (1 - lambda) * p[i+1] + lambda * p[i]
Backward pass (re-anchor root):
p[0] = root (fixed)
for i = 1 to n:
r = |p[i] - p[i-1]|
lambda = d[i] / r
p[i] = (1 - lambda) * p[i-1] + lambda * p[i]
Repeat until |p[n] - target| < tolerance (typically 3-10 iterations)
Joint angle constraint (per joint):
clamp the angle between segments p[i-1]->p[i] and p[i]->p[i+1]
within a cone of half-angle phi around the rest direction
Each pass is O(n) and trivially parallelisable. The algorithm converges reliably for reachable targets and handles any number of links. When the target is outside the chain’s reach, FABRIK stretches the chain toward it in a straight line. When joint constraints are applied, the algorithm clamps each joint angle at the end of each pass before proceeding — a local operation that requires no global re-computation.
FABRIK is widely used in games and robotics precisely because of its simplicity and speed. Unreal Engine and Unity both include FABRIK-based IK solvers in their animation systems. The simulation adds a two-bone analytical solution for comparison and an obstacle-avoidance mode that penalises joint configurations that intersect a set of circular obstacles.
III. Ray Marching — Rendering Without Triangles
🎨Ray Marching & Signed Distance Fields — GLSL Shader
Compose a scene of up to four SDF primitives (sphere, box, torus, capsule) with smooth-min blending. Tune Phong shading, soft shadows, and ambient occlusion. Runs entirely in a fragment shader.
Rasterisation — the process of projecting triangles onto a screen — is how virtually every real-time 3D image is produced. But there is another approach: for every pixel, cast a ray from the camera and find where it hits the scene geometry. This is the basis of ray tracing and its close cousin, ray marching.
Ray marching replaces explicit geometry with a signed distance function (SDF): a function that takes a point in space and returns the signed distance to the nearest surface (negative inside, positive outside, zero on the surface). To march a ray, start at the camera and take steps along the ray direction. At each step, query the SDF to find the distance to the nearest surface — you know you can safely step that far without hitting anything. If the distance falls below a threshold, you’ve hit the surface. If the accumulated distance exceeds a maximum, the ray misses.
Sphere SDF: d = |p - centre| - radius
Box SDF: q = |p - centre| - half_size
d = |max(q, 0)| + min(max(q.x, q.y, q.z), 0)
Torus SDF: q = (|p.xz| - R, p.y)
d = |q| - r
Smooth union (smooth-min):
smin(a, b, k) = -log(exp(-k*a) + exp(-k*b)) / k (exponential)
or:
h = clamp(0.5 + 0.5*(b-a)/k, 0.0, 1.0) (polynomial)
smin(a, b, k) = mix(b, a, h) - k*h*(1-h)
Normal estimation (central differences):
n = normalize(vec3(
SDF(p + eps.xyy) - SDF(p - eps.xyy),
SDF(p + eps.yxy) - SDF(p - eps.yxy),
SDF(p + eps.yyx) - SDF(p - eps.yyx)
))
Phong shading:
I = k_a * I_a + k_d * I_d * max(dot(n, L), 0) + k_s * I_s * max(dot(r, v), 0)^alpha
The smooth-min function is the secret ingredient that makes SDFs composable in interesting ways. Instead of a hard union (take the minimum of two SDFs, which creates a sharp seam), smooth-min blends the two surfaces together over a region controlled by the parameter k. Low k gives a sharp union; high k gives a smoothly blobby merge, as if the two shapes were made of clay and pressed together.
Normal estimation by central differences means you never need to derive the gradient analytically — the SDF itself tells you the surface orientation. This is why even exotic, hand-crafted SDFs automatically get correct shading, soft shadows, and ambient occlusion for free, simply by querying the same SDF from nearby points.
The simulation runs entirely in a GLSL fragment shader — one shader invocation per pixel, all in parallel on the GPU. Adding a new primitive or blending operator requires only a few lines of GLSL and immediately appears in the render without recompiling the main JavaScript. This is what makes SDF ray marching such a powerful creative medium: the scene description and the renderer are one and the same.
IV. Soccer Kick — Magnus Effect and Curving Balls
⚽Soccer Kick & Magnus Effect — Spinning Ball Trajectory
Set spin axis, spin rate, launch speed, and elevation. Simulate the trajectory in 3D including a defender wall at 9.15 m. Shows force breakdown and trajectory comparison with no-spin case.
When a ball spins as it travels, the Magnus effect pushes it sideways — perpendicular to both the velocity and the spin axis. A soccer ball struck with sidespin curves around a defensive wall; topspin kills the ball’s bounce on landing; backspin lets a ball float deceptively before dropping sharply. Every curving free kick and banana shot is a live demonstration of the Magnus effect.
The Magnus force arises because spin drags the boundary layer of air around the ball, accelerating it on one side and decelerating it on the other. By Bernoulli’s principle (or, more accurately, by conservation of momentum in the deflected airflow), the faster-moving side has lower pressure, and the pressure difference creates a net force perpendicular to the velocity vector. The magnitude depends on the spin rate ω, the ball radius r, and the velocity v.
F_Magnus = C_L * rho * pi * r^2 * |v x omega| / |omega|
C_L : lift coefficient (~0.25 for a soccer ball at match spin rates)
rho : air density (1.225 kg/m^3 at sea level)
r : ball radius (0.11 m for a size-5 ball)
v : velocity vector (m/s)
omega : angular velocity vector (rad/s)
Direction: F_Magnus is parallel to (omega x v)
Drag force (for reference):
F_drag = 0.5 * C_D * rho * A * v^2
C_D : drag coefficient (~0.25 for a smooth sphere, ~0.4 for a soccer ball)
A : cross-sectional area = pi * r^2
Equations of motion (Euler integration):
a = (F_drag + F_Magnus + F_gravity) / m
v += a * dt
x += v * dt
The simulation integrates the three forces — drag, gravity, and Magnus — using a fixed-timestep Euler method with dt = 1 ms, small enough that the trajectory is accurate to within a centimetre over a 30 m free kick. A defender wall at 9.15 m (the FIFA regulation distance) is shown in cross-section so you can see how a well-struck ball curves over the wall and back down into the goal.
At professional free-kick speeds (25–30 m/s) and spin rates (8–10 rev/s), the Magnus force can deflect the ball by 3–5 m laterally over a 25 m flight, which is why free-kick specialists spend thousands of hours learning to reproduce the exact foot-ball contact point and swing arc needed to impart the right spin vector. The simulation makes the sensitivity visible: a 10% change in spin rate produces a noticeably different trajectory.
The “knuckleball” effect — unpredictable wobbling of a ball with near-zero spin — is the opposite of the Magnus effect. With no boundary-layer asymmetry, small surface imperfections and seam orientation cause turbulent separation to switch sides randomly, producing erratic lateral forces that even the goalkeeper cannot predict. The simulation includes a zero-spin mode to demonstrate this.