# 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.
