slow roads

Slow Roads Seeds: How to Save and Share a Road

Updated 2026-09-21 · All guides

What a seed actually is here

The road you are driving on does not exist anywhere. It is not a level file, and nobody drew it. It is calculated a few hundred metres ahead of your bonnet, in the browser, from one short string of text. That string is the seed.

The mechanism is a seeded pseudo-random number generator. An ordinary Math.random() gives a different number every time, which is useless for a world you want to revisit. A seeded PRNG is deterministic: feed it the same starting value and it produces the same sequence of numbers, in the same order, forever. That is the trick behind procedural generation — you store eight characters instead of a hundred megabytes of terrain.

This build loads its PRNG as a separate top-level script, alea.min.js, before the two main bundles. Alea takes a string, hashes it character by character into three internal floating-point registers, and then churns out numbers from there. Two things follow from that, and they matter practically:

The generator is not called once. Terrain, road midline, foliage and rock scatter each build their own alea instance keyed on seed + x + y, the coordinates of the chunk being built. That is why the world is endless and stable at the same time: chunk (140, −3) hashes to the same numbers whether you reach it in your first minute or your fortieth. Nothing is stored. It is recomputed, identically, every time.

A red roadster on a two-lane road curving through green summer hills, seen from the chase camera
Everything in this shot — the ridge line, where the road cuts through it, which slopes got trees — came out of one short text seed and the chunk coordinates. Screenshot from the build running on this site.

Where the seed lives in the menu

Open the menu and look under road complexity. The field labelled generation seed is a plain text input. Type into it and the value is held locally until you press apply changes below it; the menu makes this obvious with animated arrows pointing at the button once there is an unapplied change.

The two-step is not politeness. Changing the seed means every heightmap chunk, the entire road midline and all the scatter get thrown away and rebuilt. The game reloads the world rather than trying to morph it.

Field length32 characters maximum, enforced by the input
Banned charactersspaces, and the ? character. Either one turns the field red immediately.
Empty seedalso rejected, but silently — an empty field is not marked red, the apply button simply stays inactive
Default seedgenerated from the clock: Date.now().toString(16).substring(3)
Applies onapply changes only — typing alone does nothing

That default is worth a second look. The current time in milliseconds is converted to hexadecimal and the first three digits are chopped off, leaving eight characters like c3e41f3d. It is a clock reading, not a random value — which is why every first-time seed looks like that, and why the eight-character space cycles round roughly every fifty days.

Type a seed with a space in it and the input goes red, then silently reverts when you click away. Use hyphens, or run the words together. The ? ban exists because the game uses ? as a separator in one of its own storage keys.

How your seed persists — and where it does not

The seed is written to localStorage under the key seed, every boot, and read back on the next visit. The console line Restoring seed from local storage confirms it on load. If you want to know which seed you are on right now without opening the menu, open the browser console: the game logs SEED: followed by the value during startup.

The precedence at boot is fixed and worth knowing, because it explains most confusion:

  1. A clock-derived seed is generated as a fallback.
  2. If localStorage holds a seed, that replaces it, and any saved road progress for it is restored.
  3. If the address contains ?seed=, that overrides both — the console logs Overriding seed with querystring.
  4. Whatever survives is immediately written back to localStorage.

Step four has a consequence people get caught by: opening somebody's share link replaces the seed saved in your browser. Your old world is not deleted, its progress is still filed under its own key, but you will not land back on it automatically. Note your seed down before you follow someone's link.

Being localStorage, this is per-browser and per-device. It does not sync to your phone or your other laptop, Chrome and Firefox on the same machine keep separate copies, private windows forget on close, and clearing site data wipes it along with your records and progress. There are no accounts and no server-side saves, so a seed you care about should live somewhere outside the browser.

Sharing a road with a link

The game parses window.location.search at startup, so a seed can travel in a URL:

https://slowxroads.com/?seed=longwayround

Open that and you begin on exactly the road that seed generates, from the start. It works on the first load of a fresh browser too, because the querystring outranks anything stored.

Here is the honest catch, and it is why seed sharing never caught on: the game reads the seed from the address bar but never writes it there. Press apply changes with a new seed and the URL stays exactly as it was. There is no share button and nothing in the interface that will ever hand you a link. You assemble it yourself — take the value out of the seed field or the console log, and paste it after ?seed=. Thirty seconds of work, but nothing in the menu will ever tell you it is possible.

A seed is not a drive

This is where most seed-sharing goes wrong. The seed fixes the shape of the land and the line of the road. It fixes nothing else. Hand someone a seed and they can easily end up somewhere that looks nothing like your screenshot.

The same style of road under winter weather, snow lying on the verges and a pale overcast sky
Season and weather are stored separately from the seed and never travel in a link. Snow only occurs in winter's clearSnow and snow weathers.

Four things are set independently of the seed, and each of them is saved under its own localStorage key:

Sceneconfig-scene-name — Hills or Off-world. Off-world also picks between Mars, Venus and the Moon.
Seasonconfig-scene-skin — internally called a skin: spring, summer, autumn, winter
Weatherconfig-scene-weather-index — five per season, stepped with Q and E
Road complexityconfig-scene-topographyeasy, normal or hard, shown as CASUAL / NORMAL / HARD

Road complexity is the one that changes the driving. It is not a difficulty slider bolted on top; it feeds different numbers into the generator. easy builds the heightmap from two noise octaves and gives you a road 3.2 units wide. normal adds a third octave at width 3. hard adds a fourth, narrows the road to 2.6, and shortens the smoothing window from seven samples to five, so corners arrive tighter. Same seed, three genuinely different roads. Season and weather are a separate axis again, and the guide to scenes, seasons and weather covers all twenty combinations in Hills.

The separation shows in how progress is filed. The cache key is <seed>_<scene>_<topography>, with _node and _progress appended. Change the scene or the complexity and you are on a different road with its own history. (It saves your position five nodes behind where you stopped, so you resume slightly back up the road rather than mid-corner.)

So when you pass a seed to someone, pass this much:

A rust-coloured Martian landscape with a dirt track winding between low rocky ridges under a hazy orange sky
The same seed in the off-world scene: a dirt track rather than tarmac, and a completely different look. Scene is not carried by ?seed=, so say which one you were in.

The undocumented parameters

Reading the querystring parser turns up two more parameters that appear nowhere in the interface.

?node=

Nodes are the numbered points along the generated road midline — the same counter used to save where you got to. Passing ?node= with an integer sets your starting node, so ?seed=longwayround&node=400 drops you several hundred nodes down that road instead of at the beginning. The console logs Overwriting initial node from QS when it takes. Anything that does not parse as an integer is ignored.

This is the closest thing the build has to a waypoint: find a stretch worth showing someone, note the node number, send it with the seed. The game still has to build everything between node zero and your start point, so a large number means a longer load.

?topo=

Less known still: the road complexity can be set from the address as well, with ?topo=easy, ?topo=normal or ?topo=hard. It writes straight through to config-scene-topography, so it also changes the setting for your next visit. That makes a properly reproducible link possible:

https://slowxroads.com/?seed=longwayround&topo=hard&node=400

A fourth value, flat, sits in the terrain data — zero height scale, road twenty units wide. The interface never offers it and its heightmap block is shaped differently from the other three, so treat it as a curiosity, not a feature.

The one-shot start-node key

There is a third way into a specific point on a road, and it is not a URL parameter at all. On startup the game looks for a start-node entry in localStorage. If one is there it logs Restoring starting node as, uses it as the start point, and then deletes the key — it is consumed on read, so it fires exactly once and never again. It also backs the number off by five nodes, the same five-node margin the normal save uses, so you arrive just short of the mark rather than on top of it.

Nothing in the interface writes that key. It exists so one page can hand a start point to the next without putting it in the address, and you can use it the same way from the console: localStorage.setItem('start-node', 400) and reload. It applies to whatever seed is loaded, which makes it the quickest way to jump around a road you already have open without rewriting the URL.

Scene and season have no querystring parameter at all. There is no ?scene= and no ?skin=. If your drive was in winter on Mars, the link cannot say so — you have to.

When a seed fails

Occasionally the generator paints itself into a corner and cannot extend the midline. It tries to recover by nudging the road's heading and regenerating; if that fails, you get a Critical Error overlay offering a reload with a different seed, and it tells you your distance progress will carry over. That is not your graphics card and not the hosting — it is the generator giving up on one arrangement of hills. Rare, but it is why a seed you like is worth writing down once you know it works.

Nothing in the graphics settings changes the world a seed produces — view distance and render scale only alter how much of it you can see at once. For everything you can press while driving, including the weather keys, there is the controls reference.

Worth knowing about this build

Everything above was read out of the bundle of the 2022 build, version 1.0.1, which is the copy running on this site — you can start a drive here and check any of it in the console yourself. anslo has kept developing the game since, and the current official version at slowroads.io has moved on in ways this snapshot has not; if seed handling has improved, it will have improved there. There is more on what this copy is on the about page.

Underneath it is three.js on WebGL, with no WebAssembly anywhere in the terrain path. The generation is plain JavaScript arithmetic seeded by a string you chose — the hills ahead of you were computed about a second ago, and if you write down eight characters, they will be there again.

Play Slow Roads → Free in your browser. Desktop or laptop with a keyboard.