I've been looking into generating lakes and rivers for a 2D infinite tilemap but I'm stuck with how to handle them for an infinite map.

I can use noise/heightmaps for determining the lakes and the starting points for rivers but for an infinite map I can't use the "flow downhill" or "carve out" technique because the player could come across the river at any point.

If I have a river that's a max of 200 tiles long, I'd have to calculate noise/map values for 33x33 chunks (my chunks are 6x6 tiles) around the player which feels excessive and very inefficient.

I have not found a way to make noise alone work for this, rivers would always be too circular if I find a range narrow enough and they'd encircle lakes, not extend outward.

Calculating voronoi cells and putting rivers at certain edges, with some extra noise to make the paths more natural, might work.

I just can't seem to find any good examples where they aren't working with known-dimension maps.

  • \$\begingroup\$ 1. Have bigger chunks just for river generation. 2. Choose starting points in these chunks based on hash/noise. 3. Perlin worms. 4. you don't have to keep results that are too far, because you can generate them again when needed. \$\endgroup\$
    – Sopel
    Nov 12, 2017 at 13:26

2 Answers 2


Take a look at this resource: http://martindevans.me/game-development/2015/12/11/Procedural-Generation-For-Dummies-Roads/

In short, I think you could treat a river in a similar manner as a road in that example. Let's say you are using simple perlin-noise as a heightfield. You can then sample that heightfield at any point to generate an uphill/downhill vector for that location in space using a formula like the one listed below, and you can then use that vector to trace out the route of a river. E.G. start at a random seed position, sample the vector field, step in the direction of the vector, iterate like this until the route has traveled a certain number of steps, or doubles back on itself, or goes uphill, or collides with another route, etc. As will all things procedural, it will take a lot of fiddling to achieve a believable result.

heightfield-to-vectorfield pseudocode:

float v = SampleHeightField(position);
float x = SampleHeightField(new Vector2(position.x + .5f, position.y));
float y = SampleHeightField(new Vector2(position.x, position.y + .5f));

Vector2 fieldVector = new Vector2(v-x,v-y);
  • \$\begingroup\$ This is primarily what I was already considering: "start at a random seed position, sample the vector field, step in the direction of the vector, iterate like this until the route [ends...]", and I may just have to, but what if I want rivers to stretch for 1000 tiles? At the very minimum I'd have to look for any starting positions within 27,556 (166x166) chunks which is inefficient. I may just have to live with small rivers for now but I was hoping there was a more efficient way \$\endgroup\$
    – BotskoNet
    Oct 10, 2017 at 14:38

I had this problem, my solution was to replace causation with correlation. Basically all algorithm try to cause a river, ie you have a mountain and you try to find a plausible river path, which is hard. But observation show that river correlate to a the slope of a mountain, ie it flows down.

SO why not generating the river first THEN generating the mountain that generate it? Basically the river define the slope of the mountain. It gives you much more power because you can now control events along the river path, it become trivial to add lake, bridge, waterfall, town, just like you generate barrier, lighting, etc ... along a road.

Basically all you need now, is the starting point (highest point) and the ending point (lowest point), which you can use to join river together too, and if you are smart you can also create infinite river. You can just recursively break the river in smaller intermediate segment or events, and just interpolate between them. Adjusting the width of influence to shape terrain around it.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .