build stuff

Project · ★★★ · Advanced

Driftworld

A whole 3D world, grown from one word. Type a seed like "mossy-otter" and hills, lakes, beaches, snowy peaks and forests appear, going on as far as you walk. The sun sets, the fog rolls in, the real constellations come out and name themselves when you look at them. Pick a season and a look: flat and fast, or shadows and glossy water. Walk or fly with a compass and a minimap, find the stone rings, monoliths and towers, plant flags where you find something good, and save it: your friend opens the link and stands in the exact same world, with your flags and a picture of the view you saved.

Walk around in it The finished world, built from the brief below. Here it runs from a seed only; saving a world needs your own Worker.

You need

What you'll learn

What this shows the agent can do

The brief

Make a new folder for the project. Copy the brief below into a file called BRIEF.md in that folder, or download BRIEF.md. Open the agent in the folder and say:

Read BRIEF.md and start with milestone 1. Stop after each milestone so I can check it.
# Driftworld — a procedural 3D world you can share (★★★)

Build a 3D world explorer in the browser with Three.js. You type a seed, any word like `mossy-otter`, and the game grows a whole landscape from it: hills, lakes, beaches, snowy peaks, forests of two kinds of tree, boulders, and now and then a stone ring, a monolith or a tower to find, going on as far as you walk. The same seed always makes the same world, so a seed is a world. The sun rises and sets, clouds drift, a moon comes up. You can walk on the ground or fly, with a minimap in the corner. You can plant markers with short labels where you find something good. Save the world and you get a link: your friend opens it, gets the same land, your markers and a screenshot of the view you saved. As an optional extra, you can describe a world in words and a model on the server turns the description into settings for the generator.

## Done looks like

- A start screen with a seed box, a "Random seed" button, a Season selector (spring, summer, autumn, winter) and a Look selector ("Paper: flat and fast" or "Real: shadows and glossy water"), a small map of the seed's land that redraws as you type and follows the season, and a list of worlds on this device, each with a Delete button that drops it and its flags from this device.
- The terrain is made from seeded noise: same seed, same world, on any computer. A different seed is clearly a different place.
- The world is built in chunks around you, made as you move and thrown away when far behind, so you can walk for ten minutes without the frame rate dropping or memory climbing.
- Colours by height, slope and a tint noise: water, wet sand, sand, light and dark grass, a darker floor under trees, dirt and rock on slopes, snow above a snow line that is patchy at its edge. A little speckle so flat ground is not one flat colour.
- Two kinds of tree with trunks: conifers higher up and in cold spots, broadleaves lower down. Trees placed by their own noise, not on steep slopes or under water, each tinted a little differently. Boulders on steep or high ground.
- Seasons: the season moves the snow line (34, 40, 30 and 14 units for spring, summer, autumn and winter), tints the grass (fresh green, plain, straw, pale grey-green), colours the broadleaf canopies (light green, green, orange, and in winter a small brown tangle for bare branches: bake the colour into the tree shape, a tint multiplied on green cannot reach orange) and cools the sea. The sun's path sits lower in the sky in autumn and winter and its light is bluer and dimmer. The season is in the link (`&season=winter`) and shown after the seed name in the top bar.
- Looks: "paper" is the flat look: Lambert materials, no shadows. "real" uses physically based materials (`MeshStandardMaterial`), soft shadow maps from the sun (a 2048 map over a 260 unit box that follows the camera, so hills, trees and towers all cast and receive shadows), ACES filmic tone mapping, a glow round the sun in the sky dome, and a sea with a tiling ripple normal map that stays put in the world and creeps slowly. Phones default to paper, laptops to real. The look is in the link (`&look=paper`).
- Landmarks: in about one chunk in twelve, on flat dry ground, a stone ring, a monolith or a tower. A short message names it when you reach it on the ground ("Tower. Press M to flag it.", or "Tap Flag to mark it." on a touch screen), and the flag label is filled in with its name. From more than 12 units up the message only says "Tower below."
- A day and night cycle of 10 minutes: the sun moves and reddens near the horizon, the sky is a dome shaded from horizon to top, light and sky colour change, fog thickens at dusk, clouds drift overhead and take the sky's colour, a moon rises opposite the sun, stars come out at night. The bright stars are about thirty real constellations (Orion, the Big Dipper and Little Dipper, Cassiopeia, Leo, Scorpius, Cygnus, the Southern Cross, Sirius and the rest of the well-known ones) placed from their right ascension and declination, with faint lines between them, and the name of the figure in the middle of the view appears under the crosshair at night, and the whole sky turns once a day round a pole that stands as high as the world's latitude, which the seed picks. A time slider.
- A compass tape at the top of the screen with N, E, S, W and the points between, a red needle in the middle and the heading in degrees. North is where the sky's pole leans and the sun rises in the east. You start facing east just before dawn, so the first thing you see is the stars going and the sun coming up.
- A round minimap in a corner, drawn from the height function in the same colours as the land, with an arrow for you and your heading and dots for your flags. It redraws twice a second.
- Walk with WASD and mouse look on a laptop; on a phone, a left thumb joystick and drag on the right to look. F (or a button) toggles fly.
- Place a marker where you are looking, with a label up to 30 characters, only when the spot is within 40 units; from high in the air it asks you to come closer. Markers show as small flags with their label.
- Without any server, `?seed=mossy-otter` opens that world.
- "Save and share" stores the seed and markers in D1 and a screenshot in R2, and gives a link like `/?w=7fk2qd`. Opening it loads the same world, the markers and shows the screenshot first.
- 60 frames per second on a laptop, 30 or more on a mid-range phone.
- No console errors.

## Constraints

- Three.js from a pinned CDN address in an import map, for example `https://cdn.jsdelivr.net/npm/three@0.170.0/build/three.module.js` and add-ons from `https://cdn.jsdelivr.net/npm/three@0.170.0/examples/jsm/`. Never "latest". No other libraries, no build step.
- Seeded randomness: hash the seed string to a number, feed a small generator (for example mulberry32), and write the 2D noise function in the project. Never use `Math.random()` for anything that shapes the world.
- Chunks of 64 by 64 units. Each tree kind is one shape merged from primitives with vertex colours (`BufferGeometryUtils.mergeGeometries`), drawn as one `InstancedMesh` per kind per chunk, tinted per instance with `setColorAt`. Boulders the same way. Dispose geometry and materials of dropped chunks. Cap the pixel ratio at 2.
- The minimap and the start-screen preview share one map-drawing function that samples the height function onto a 2D canvas. No second copy of the terrain.
- Server only for saving: one Cloudflare Worker with static assets, a D1 binding and an R2 binding. Use wrangler. Free plan only. The game must still run with `?seed=` if the Worker is down.
- D1 table `worlds(id, seed, name, markers, created_at)` with `markers` as JSON text. R2 key `shots/<id>.jpg`.
- The Worker checks everything: seed up to 40 characters, name up to 40, at most 100 markers, labels up to 30 characters, numbers finite, screenshot JPEG and under 1 MB. World ids are 6 random characters.
- No accounts, no tracking. Phone works.
- The optional AI step (milestone 8) is clearly labelled in the game as optional. It needs Workers AI turned on in your Cloudflare account, or an API key kept as a Worker secret. The browser never calls a model directly and never holds a key.

## Milestones

1. **A scene.** `index.html` with the import map, a renderer, a camera, a sun light and a spinning cube. Resize handled.
   Check: a lit cube spins smoothly, and the browser's network panel shows Three.js loaded from the exact pinned address.
2. **Seeded terrain.** One chunk of terrain from the seed with height colours and a water plane. Print `height(10, 10)` for seed `test` in the console.
   Check: reload three times: the same number prints and the same hills appear; change the seed and the land changes.
3. **Walk and fly.** Walk on the surface with mouse look (Pointer Lock) and the phone controls. F toggles fly.
   Check: you can walk up a hill without sinking into it, fly over it, and do both on a phone.
4. **Endless land, trees, rocks and landmarks.** Chunks around the player, built and dropped as you move. Two tree kinds and boulders with `InstancedMesh`. A landmark in about one chunk in twelve.
   Check: walk in one direction for five minutes: the frame rate holds and the chunk count in a debug corner stays flat. You pass conifers up high, broadleaves down low, rocks on the slopes, and at least one stone ring, monolith or tower.
5. **Day and night, seasons and looks.** Moving sun, a sky dome shaded from horizon to top, fog, drifting clouds, a moon and stars, plus a time slider.
   Check: drag the slider through a full day: the light goes from dawn to noon to a red dusk to a dark night with stars and a moon, and the clouds take the sky's colour. Open the same seed in winter: snow comes down the hills and the broadleaves are bare. Open it with `look=real`: trees throw shadows on the ground and the sea glitters under a low sun. During the night you can pick out Orion or the Big Dipper, their names appear when you look at them, and they move across the sky.
6. **Markers, minimap, compass and seed links.** Place labelled markers by raycasting the terrain. Keep them in localStorage per seed. A minimap drawn from the height function, with your heading and your flags. A compass tape at the top, with the heading in degrees. `?seed=` opens a world. The start screen previews a seed's map as you type, and each world on the device has a Delete button.
   Check: plant three markers, reload: they are in the same places and on the minimap; send `?seed=` to another browser and it shows the same land; delete a world and it is gone from the list, flags included.
> Up to here the world lives on your own device, and a seed link already lets a friend see the same land. Save and share puts your markers and a screenshot online too.
7. **Save and share.** The Worker, D1 table and R2 bucket. `POST /api/worlds` saves seed, name and markers and returns an id. `PUT /api/worlds/<id>/shot` takes the screenshot. `GET /api/worlds/<id>` loads it. Deploy.
   Check: save a world, open the link on your phone over mobile data: the screenshot shows, then the same land and your three markers.
8. **Optional: describe a world.** A text box, labelled "Optional, needs Workers AI or an API key". The Worker sends the text to a model and asks for JSON only: `treeDensity` 0 to 1, `waterLevel` -1 to 1, `mountainHeight` 0 to 2, `palette` from a fixed list, `landmarks` a list of up to 5 items each `tower`, `ring` or `monolith`. The Worker clamps every value and drops anything else before the game uses it.
   Check: "a flat desert with three stone rings" gives low hills, no water, sand colours and three rings; nonsense text still gives a valid world.

## Stretch

- Caves or overhangs using 3D noise for some chunks.
- Weather: rain particles and darker fog, chosen by the seed and the time.
- A photo mode that hides the controls and lets you tilt the camera.
- Footstep and wind sounds with Web Audio.
- A gallery page of recently saved worlds, newest first, from D1.

## Leave out

- Accounts, multiplayer, chat.
- Physics engines, model files, textures downloaded from the internet.
- Any model call from the browser, and any API key in front-end code.

Start by reading WRITING.md and the guide chapter on data if you need storage.

Milestones

The same milestones as the brief. Tick each one off when its check passes. Do one per sitting if you like. The dot in the list marks the point where you have something that works on its own. Everything after it makes it remember things or work for more than one person. Stop at the dot and use what you built before you go on.

  1. A scene. index.html with the import map, a renderer, a camera, a sun light and a spinning cube. Resize handled.

    Check: a lit cube spins smoothly, and the browser's network panel shows Three.js loaded from the exact pinned address.

  2. Seeded terrain. One chunk of terrain from the seed with height colours and a water plane. Print height(10, 10) for seed test in the console.

    Check: reload three times: the same number prints and the same hills appear; change the seed and the land changes.

  3. Walk and fly. Walk on the surface with mouse look (Pointer Lock) and the phone controls. F toggles fly.

    Check: you can walk up a hill without sinking into it, fly over it, and do both on a phone.

  4. Endless land, trees, rocks and landmarks. Chunks around the player, built and dropped as you move. Two tree kinds and boulders with InstancedMesh. A landmark in about one chunk in twelve.

    Check: walk in one direction for five minutes: the frame rate holds and the chunk count in a debug corner stays flat. You pass conifers up high, broadleaves down low, rocks on the slopes, and at least one stone ring, monolith or tower.

  5. Day and night, seasons and looks. Moving sun, a sky dome shaded from horizon to top, fog, drifting clouds, a moon and stars, plus a time slider.

    Check: drag the slider through a full day: the light goes from dawn to noon to a red dusk to a dark night with stars and a moon, and the clouds take the sky's colour. Open the same seed in winter: snow comes down the hills and the broadleaves are bare. Open it with look=real: trees throw shadows on the ground and the sea glitters under a low sun. During the night you can pick out Orion or the Big Dipper, their names appear when you look at them, and they move across the sky.

  6. Markers, minimap, compass and seed links. Place labelled markers by raycasting the terrain. Keep them in localStorage per seed. A minimap drawn from the height function, with your heading and your flags. A compass tape at the top, with the heading in degrees. ?seed= opens a world. The start screen previews a seed's map as you type, and each world on the device has a Delete button.

    Check: plant three markers, reload: they are in the same places and on the minimap; send ?seed= to another browser and it shows the same land; delete a world and it is gone from the list, flags included.

  7. Up to here the world lives on your own device, and a seed link already lets a friend see the same land. Save and share puts your markers and a screenshot online too.
  8. Save and share. The Worker, D1 table and R2 bucket. POST /api/worlds saves seed, name and markers and returns an id. PUT /api/worlds/<id>/shot takes the screenshot. GET /api/worlds/<id> loads it. Deploy.

    Check: save a world, open the link on your phone over mobile data: the screenshot shows, then the same land and your three markers.

  9. Optional: describe a world. A text box, labelled "Optional, needs Workers AI or an API key". The Worker sends the text to a model and asks for JSON only: treeDensity 0 to 1, waterLevel -1 to 1, mountainHeight 0 to 2, palette from a fixed list, landmarks a list of up to 5 items each tower, ring or monolith. The Worker clamps every value and drops anything else before the game uses it.

    Check: "a flat desert with three stone rings" gives low hills, no water, sand colours and three rings; nonsense text still gives a valid world.

Make it yours

Once it works, pick one of these and ask for it as a new milestone.

If it goes wrong

The frame rate falls the longer you walk.

Old chunks are hidden but never disposed, so memory keeps growing. Say this to the agent:

The game gets slower the longer I walk. Show a debug corner with chunk count and renderer.info memory, and dispose geometry and materials of chunks that are dropped.

The shared link shows different land than you saved.

Something in generation uses Math.random() or depends on the order chunks were built. Say this to the agent:

My friend sees different land from the same seed. Find every use of Math.random() in world generation and replace it with the seeded generator, keyed by chunk position.

Three.js fails to load with an import error.

The import map is missing, or an add-on is loaded from a different version. Say this to the agent:

Three.js fails to load. Show me the import map and make every Three.js address use the same pinned version.