OCEAN LABfreediving · Blender → three.js

isda's ocean in three.js · FFT swell · caustics · light shafts

Freediving
Ocean Lab

Four dive-site scenes on the ocean from the isda project: a JONSWAP swell on FFT cascades, water that takes the red out of the light first, and caustics and light shafts made by the same waves. Seven freedivers from four pipelines are in the water.

Oceanbuilding the sea
Surface0.0 m

– fps

Fin studio · Mootion bot, Nov 2023

Long fins,
one breath.

The 2023 Mootion bot, re-rigged in Blender with four freediving fins. Each blade has its own bone chain, so it bends with the kick. Swap fins and scrub the stroke here, then switch to Dive and take it down the line.

0%loading diver.glb

Seven freedivers, four pipelines

The divers

Rigged figures from the Fin Lab, the isda game, a MakeHuman freediver and a Hunyuan 2 generation. All of them run in the ocean above on their own clips, aligned to one swimming frame from their own hips, head and thigh bones.

May 2025 · Hunyuan 2 · thereceegoes

Generated freediver

An image-to-3D model made with Hunyuan 2 in May 2025 from the thereceegoes photo, then rigged with a Mixamo skeleton. The long white bi-fins are part of the generated mesh and follow the shin and foot bones, so the kick here is procedural. The hips and knees swing about the hip line, with the legs in antiphase.

5.4 MBloads when this section is on screen
2025-05 · Hunyuan 2

Motion

Input photo · thereceegoes
Hunyuan 2 output · turntable
Made
May 2025 · Hunyuan 2, image-to-3D
Source
freediver-thereceegoes.png
Rig
Mixamo skeleton, 22 joints
Mesh
500k → 80k triangles for the web
Maps
colour 2048 · metal/rough + normal 1024, WebP
File
31.8 MB original → 5.4 MB GLB

Four fins, one rig

All four fins share the diver's skeleton. Pockets are weighted rigidly to the foot and the blade blends along its own chain. The numbers below are the modelled geometry, plus the kick values the dive uses. Click a row to load that fin.

FinBlade lengthSpanBlade angleChainKickRateThrustO₂ while kicking

Rendered in Blender

Cycles stills and EEVEE loops rendered straight from the same file the GLB is exported from. The water is a volume box with a caustic-textured spot light above it.

Straight from the viewport

Screenshots of the Blender 5.2 UI with the scene set up by viewport_shots.py. Fin chains use their own bone colours, and the weight paint shows how one chain bone's influence fades along the blade.

How the fins are rigged

A long blade can't follow the foot rigidly. Its tip trails behind the kick and snaps through at the turn of each stroke. So each blade gets a chain, and the kick drives the chain with the foot's velocity, delayed a little more at each bone.

Foot_L (pocket rigid) _1_2_3_4_5 knee / ankle drive the chain root tip lags ≈ 0.45 + 0.28·i rad weight blend along s (m) 0.040.200.360.520.690.86 Foot→ tip
  1. Import and normaliseThe Mootion FBX comes in at 1/100 scale under a rotated empty. That transform is baked into the bones and meshes, so armature space is world space in metres.
  2. Re-rest into a swim poseLegs adducted to 12 cm between ankles and feet pointed 58°, then applied as the rest pose. The original 298-frame clip is re-baked onto the new rest.
  3. Model the fins around the flat footPockets are lofted around the original foot bounds and each blade is swept at its angle (18–25°). Everything is then mapped through the foot's rest change.
  4. Add a chain per bladeLong carbon 5 bones, training 3, delta 4, competition 5. Chains are parented to Foot_L / Foot_R and rolled so local X bends the blade.
  5. WeightPocket vertices go 100% to the foot. Blade vertices blend linearly between neighbouring chain bones by distance along the blade.
  6. Author the kicksFlutter kick with the legs in antiphase for bi-fins. Dolphin kick with a body wave for monofins. The blade flex is the foot's velocity with a growing delay down the chain.
  7. Export and scale liveOne GLB carries 7 clips and 90 joints. The browser reads every fin bone after the mixer runs and scales its rotation with the Blade flex slider.