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:
- SVG feTurbulence gives us perlin noise. Let’s use red channel for elevation and green channel for moisture.
- SVG feColorMatrix gives me a way to convert (elevation,moisture) to browns and greens
- SVG feComponentTransfer lets me do thresholding. When elevation < some_value, alpha = 1; else alpha = 0. This will be the water layer.
- SVG convolve filter gives me an emboss effect
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.