In 2026 public demos of Claude building 3D scenes in the browser started to circulate among developers. We ran our own experiment to see how far one request goes: The Valley, a 76-second 3D film with a stone viaduct, a steam train and a village, written by Claude Opus 5.5 as a single HTML file. You can watch it as a video or open it live in the browser.
This post covers what is in the file, how each part works, how we checked it, and the one bug that only appeared on some computers.
What we built
The film is 76 seconds long and has six shots:
- The camera rises out of the grass on a hillside with sheep, and the title appears.
- A wide aerial view of the valley. The train approaches from the western hills with a trail of smoke.
- A low angle from the canal bank: the train crosses the viaduct overhead.
- A tracking shot alongside the locomotive on the viaduct.
- A flight along the canal through the village, toward the viaduct.
- A crane shot that pulls back to the whole valley, with the end card.
Everything on screen comes from code: the terrain, about 2.08 million grass blades, trees, sheep, the viaduct, rails and sleepers, the train with four carriages, cottages with thatched roofs, a church, narrowboats, water, smoke, sky and clouds. The soundtrack is synthesised in the browser as well. The file is 1,474 lines. The only library is Three.js r185.
Why one HTML file
A single file has practical advantages for this kind of work.
- It opens anywhere. A double click, a link or an upload to any static host is enough. There is no build step and no asset server.
- The model sees the whole project at once. Claude can read the full scene, change the camera in one place and the smoke in another, and keep them consistent.
- Every change is a text diff. You can review it, roll it back and compare versions.
- Loading is light. The page downloads a small HTML file and Three.js; the rest is computed on the visitor's machine.
On mmix.biz one extra step was needed. The site's Content Security Policy allows scripts and fonts only from its own domain, so Three.js and the two fonts are stored next to the film instead of coming from a CDN.
How the scene is made
Terrain and railway
The land is a 3 by 3 km height field built from simplex noise on a 750 by 750 grid. A valley profile runs along a meandering canal, rolling hills rise on both sides, and a ring of ridged-noise mountains closes the horizon.
The railway is a spline across the valley. Along it the terrain is pulled toward the track level, which produces embankments and cuttings. Where the natural ground drops more than about a metre below the rails, the code builds the viaduct instead: it measures the gap, divides it into equal arch spans and extrudes one stone wall with arch-shaped holes.
Two million grass blades
One blade is five vertices. The scene draws it about two million times with instancing, and each copy carries its position, rotation, height, width and colour. Wind runs in the vertex shader: a slow noise field for gusts, a quick per-blade flutter, and a bend that grows with the square of the height along the blade.
The shader is injected into a standard Three.js Lambert material, so the grass receives the same shadows and fog as everything else. A small backlight term makes the blade tips glow when the camera looks toward the sun. The grass sits on a separate render layer, and the water reflection pass skips it, which keeps reflections cheap.
Light, fog and grading
The sun hangs low in the north-west, so most shots look into the light. We replaced the fog code that Three.js injects into every material: the fog colour shifts toward a warm tone where the view ray points at the sun. Distant ridges near the sun turn golden, and the ones in the other direction stay cool.
After the scene is drawn, a bloom pass adds glow around bright areas, and a final grading pass adds a slight colour fringe at the edges, split toning, a vignette, film grain and 2.39:1 letterbox bars.
A shot-list camera
The camera works like a shot list. Each shot has a time window, a lens angle, a shadow area and a function that returns the camera position and the point it looks at. Moves are eased, a small handheld drift is added, and the camera is never allowed below the ground.
The tracking shot is computed from the train itself: the camera orbits the locomotive's current position on the rails. The shadow map follows each shot, and its centre snaps to whole shadow texels so the shadows do not shimmer while the camera moves.
Train, smoke and sound on one clock
The train's position is a function of film time: the distance travelled along the track at a fixed speed. That makes every frame reproducible, which matters for video export.
The wheels turn by distance divided by radius. Every quarter turn of the driving wheels emits a puff of smoke from the chimney and two bursts of steam from the cylinders. The same event plays a chuff in the soundtrack. Smoke particles slow down into the wind, grow and fade over about ten seconds.
The sound is built with WebAudio: filtered noise for the chuffs, short clicks on rail joints every 18 metres, a three-tone whistle, wind, birdsong when the camera is close to the ground, and quiet chords with bell notes through a generated reverb. The train's volume and stereo position follow its distance and direction from the camera.
How we checked it
Every judgement about the film was made on rendered frames. The file has a capture mode: a URL parameter selects any moment of the film, and a small script hook renders it exactly. A headless Chromium on the studio workstation, with GPU access through ANGLE and Vulkan, took stills of every shot.
The first review found problems a reader of the code would miss: mist layers that turned the valley milky, cottage walls that clipped to white, colour fringes that were too strong, a canal that looked like a concrete gutter, and chimney smoke sitting on the roofs like cotton balls. Each round of fixes was followed by new stills.
Then the full film was rendered: 2,280 frames at 1920 by 1080 and 30 frames per second, in about three to four minutes. A contact sheet of frames across the whole film checked the edit and the motion. In real time the scene holds about 60 frames per second on an RTX 3060.
The bug that only some computers saw
After the first version went out, the owner reported black flashing in the middle of the film. Our video had no black frames. The difference turned out to be the graphics backend: our tests ran on Vulkan, and the flicker appeared on OpenGL.
We reproduced it by playing the film in real time on the OpenGL backend and sampling pixels every frame. From the eleventh second onward, more than 1,000 of about 4,600 frames came out black.
The cause was one line of arithmetic. A smoke particle's age ratio could go slightly above 1 on its final frame. Raising a negative number to a fractional power returns NaN in JavaScript, so the particle got an invalid size and transparency. On OpenGL that particle still drew a pixel. The bloom pass then blurred the invalid pixel across large parts of the frame.
The fix clamps the ratio to 1 and makes the shader discard any fragment whose transparency fails a comparison, which also catches NaN. The same probe then showed zero dark frames. The practical lesson is to test real-time playback on more than one graphics backend, since offline capture can look clean while a live browser does not.
What it took to publish
To put the film on this site, we:
- rendered a version without burned-in titles, so one video serves all five languages;
- encoded it in two passes at about 1.9 Mbit/s: 18 MB, under the 25 MB per-file limit of Cloudflare Pages;
- made a page in five languages with VideoObject and FAQ structured data;
- added the 3D version with a language switch and a lighter grass setting for phones.
When to use this approach
Each tool has its own strengths.
- One-file Three.js scene. A stylised scene that loads fast on a web page, runs live in the browser and exports to video. Limits: a stylised low-poly look, no film-grade ray tracing, and a heavy load on older phones.
- Blender. Photoreal rendering, detailed modelling and offline video. Claude can also drive Blender through a Model Context Protocol server.
- Unity or Unreal. Large interactive worlds with physics, animation tools and asset pipelines, with bigger downloads.
For a product hero, a venue fly-through, an event trailer or a short loop for social media, a one-file scene is often enough. We build this kind of work as part of our AI content systems.
The film, the video and a link to the live 3D version are on the Valley experiment page.

