Generating maps with image filters

 from Red Blob Games
21 Nov 2014

This map is made in pure SVG -- no Javascript! View the source if you don’t believe me! :-) ; doesn’t work in all browsers, and works differently across some browsers, sadly. (There is javascript to change the seed and change the parameters using DAT.gui, but the map itself is made with svg filters: feTurbulence, feComponentTransfer, feColorMatrix, feConvolveMatrix)

Note that this is a generic map renderer. Given two grayscale images with elevation (heightmap) and moisture, it will use SVG filters to turn them into what you see above. For the demo I use SVG’s perlin noise to generate both the elevation and moisture images.

Maybe I should’ve used GL shaders, but I wanted to see what SVG could do with filters:

The matrix trick works by defining a color vector for elevation (0 elevation to 1 elevation) and another color vector for moisture (0 to 1), and then adding them for the actual elevation and moisture. Most color tables aren’t linearly separable like this. I picked some colors for the four corners of the table, and then I tried to come up with some vectors that approximated those colors. They’re pretty close, so I got lucky!

    Color square from mapgen2
    E=0 M=0 210 185 139  subtropical desert
    E=1 M=0 201 210 155  temperate desert
    E=0 M=1  68 136  85  tropical rain forest
    E=1 M=1 153 170 119  taiga

    Vectors on the four sides of the square
    At M=0 E vector is 210   -9, 185 +25, 139 +16
    At M=1 E vector is  68  +85, 136 +34,  85 +34
    At E=0 M vector is 210 -142, 185 -49, 139 -54
    At E=1 M vector is 201  -48, 210 -40, 155 -36

    I can approximate this with
    Base is      210  185  139
    E vector is  +40  +30  +25
    M vector is -100  -45  -45
    
    Reconstructed values at the four corners of the square:
    E=0 M=0 210 185 139 -- exact match, by definitions
    E=1 M=0 250 215 164 -- too red?
    E=0 M=1 110 140  94 -- too red?
    E=1 M=1 150 170 119 -- just right

    After plugging these initial values in, the colors seemed too
    boring, so I tweaked the values in the matrix until I got
    something I liked.

In GL, we could do this with a texture lookup instead. It gives us a lot more flexibility. You’d embed the Whittaker Diagram colors into a texture and then use u = elevation, v = moisture as the lookup coordinates. You can directly include water in the texture instead of the separate image I used for the SVG diagram.

If this kind of thing fascinates you, check out Toby Schachman’s Shadershop[1] and this libnoise tutorial[2]. In 2021 I found this amazing demo[3] of svg filters for map generation.

Email me , or tweet @redblobgames, or comment: