Motion Fossil was my first Three.js experiment, and it runs entirely inside a React Native app: your finger trace becomes a live 3D ribbon, and when you release, it crystallizes into a metallic sculpture you can spin around. Here is what it took.
The Metro part nobody tells you about
react-native-webgpu gives you a real WebGPU surface on iOS. But Three.js
ships its WebGPU renderer under a separate entry, and @react-three/fiber
needs its module build on native. Both are Metro resolution problems:
config.resolver.resolveRequest = (context, moduleName, platform) => {
if (moduleName.startsWith('three')) {
moduleName = 'three/webgpu';
}
if (platform !== 'web' && moduleName.startsWith('@react-three/fiber')) {
return context.resolveRequest(
{
...context,
unstable_conditionNames: ['module'],
mainFields: ['module'],
},
moduleName,
platform,
);
}
return context.resolveRequest(context, moduleName, platform);
};Without the alias, three resolves to the WebGL build and nothing renders.
Without the condition names, fiber pulls its CommonJS build and crashes at
import time. Add the react-native-webgpu plugin to app.json and build a
custom dev client, it does not run in Expo Go.
One ribbon buffer, zero allocations
The trace is a single pre-allocated BufferGeometry sized for 180 points.
Gesture Handler samples the finger every 5 pixels and maps position plus
velocity into 3D points, fast strokes dive deeper into the scene. Each frame
writes four vertices per point around the path tangent and grows the draw
range:
positions[offset] = point.x + normalX;
positions[offset + 1] = point.y + normalY;
positions[offset + 2] = point.z + halfDepth;
// ...three more vertices per point
geometry.setDrawRange(0, count * INDICES_PER_SEGMENT);
geometry.computeVertexNormals();The ribbon extends under your finger without ever rebuilding the mesh, which is what keeps the trace at 60fps while WebGPU is rendering reflections.
Crystallize on release
While you draw, the material is emissive clay: low metalness, high roughness, a warm glow. On release, one medium haptic commits the fossil and three material properties animate together:
material.metalness = 0.12 + crystallize * 0.72;
material.roughness = 0.72 - crystallize * 0.46;
material.emissiveIntensity = 0.42 - crystallize * 0.3;Clay becomes metal, the glow dies, and the reflections take over. Further drags rotate the preserved object instead of drawing.
Try it
npx @rslab/cli add motion-fossilThe full source lives in the registry, and the interactive demo is on rselmi.com/lab/motion-fossil.