How to generate custom biomes in Azgaar’s Fantasy Map Generator
Azgaar’s Fantasy Map Generator is (in my humble opinion) one of the best tools for worldbuilding out there. While it does not currently simulate seasonal changes, its annual-based temperature and precipitation simulation is still a convenient time saver for worldbuilders.
The generator comes with 12 default biomes that it can automatically generate on-demand. You can add custom biomes and manually assign them to cells, but unfortunately, there is no built-in option to generate custom ones. Thankfully since the generator is almost entirely clientside (run locally on your browser), you can generate custom biomes using Javascript in your browser’s Console.
If you are unfamiliar with using the Console on Chrome (the same browser I use), then please check out Google’s help article for it. Otherwise, for users of different browsers, you’ll have to figure that one out elsewhere. Most browsers probably have some form of console tool.
Disclaimer
I have not personally encountered any file-breaking bugs generating custom biomes, but it is best to be safe and save your map in a separate file. As always, use this tutorial at your own risk. The code used to generate custom biomes is not provided natively in the generator. I try my best to make it as practical and straightforward as possible, but if you have a better idea or way of generating custom biomes, then be sure to share.
Generating one biome at a time
Before generating a custom biome, we need to define its function via the Console. To do this, paste and enter the following Javascript code in your browser’s Console.
function generate_custom_biome(index, min_height, max_height, min_temp, max_temp, min_prec, max_prec) {
for (const i of pack.cells.i) {
if (pack.cells.h[i] >= min_height && pack.cells.h[i] <= max_height && grid.cells.temp[pack.cells.g[i]] >= min_temp && grid.cells.temp[pack.cells.g[i]] <= max_temp && (grid.cells.prec[pack.cells.g[i]] * 100) >= min_prec && (grid.cells.prec[pack.cells.g[i]] * 100) <= max_prec) {
pack.cells.biome[i] = index;
}
}
}
Don’t worry; this code doesn’t do anything by itself. Before running the function, let’s go through what its parameters are. Keep in mind, using each of these parameters is mandatory.
index
Rather than identify biomes by their name, the generator refers to their index. Since there are 12 default biomes, any custom biome you add will always have an index of 13 or more. Enter the following in the Console as it appears exactly (do not replace “name” with the biome’s name) to list the current biomes and their indices. Entering this is optional and can be done at any time and multiple times to confirm the indices.
biomesData.name
You may have noticed a “Marine” biome at index 0. Please do not attempt to generate this biome or mess with it in any way. Visible biomes in the generator are terrestrial only, and the “Marine” biome is used only by the generator to identify water cells. Technically speaking, there are 13 default biomes, but the “Marine” biome is not displayed.
The index of the biome you want to generate is what you use in the function’s index parameter. For example, if you want to generate the “Tundra” biome, you would use 10.
min_height
This parameter determines the minimum height required for a biome to generate. Use a value anywhere from 20 to 100 (do not use a value below 20), keeping in mind that the generator considers 70 or higher mountains. To find out what the height value exactly translates to in absolute units, view the height of a cell in brackets beside the Elevation measurement in the Cell Details tool panel (go to Tools -> Click to overview: -> Cells).
max_height
This parameter determines the maximum height permitted for a biome to generate. Use a value equal to or greater than the min_height.
min_temp
This parameter determines the minimum temperature (ºC) required for a biome to generate. Use a value from -50 to 50.
max_temp
This parameter determines the maximum temperature (ºC) permitted for a biome to generate. Use a value equal to or greater than the min_temp.
min_prec
This parameter determines the minimum precipitation (mm) required for a biome to generate. Use a value from 0 to 9999 (you may also enter higher values, but I’ve personally not encountered precipitation higher than 9,999 mm on my maps).
max_prec
This parameter determines the maximum precipitation (mm) permitted for a biome to generate. Use a value equal to or greater than the min_prec.
Adding a custom biome
The function provided above does not create a custom biome in itself but instead assigns applicable cells to the biome. If you wish to generate a custom biome, you must add a custom biome first using the Biomes Editor. This editor can be found under Tools -> Click to configure: -> Biomes.
If you have already created a custom biome or several, they will appear listed using biomesData.name. In either case, you can enter that into Console to confirm the biome indices.
Using generate_custom_biome
It’s finally time to use the function. To do this, copy and paste the following and replace the parameters with appropriate values. Once ready, hit enter in the Console.
generate_custom_biome(index, min_height, max_height, min_temp, max_temp, min_prec, max_prec)
An example of the above is below.
generate_custom_biome(13, 20, 69, 10, 18, 600, 1199)
Once you’ve run the function, refresh the Biomes Editor by clicking on the spinning arrow button on the far left near the bottom of the panel. Also, change the Habitability to refresh the population calculation in the assigned cells. For example, even if you want to keep the default Habitability (50%), you still have to change it temporarily to force the editor to refresh the population. To see the applied changes, toggle on and off Biomes under Layers. All applied changes should save properly at this point the next time you save the map file.
You can repeatedly run the function without defining the function again during your current session (as long as the browser tab is open and not refreshed by the browser). You will need to define the function again for new sessions before running it.
Biome names and colours can be changed using the Biomes Editor without issue; this will not affect the function as it uses a biome’s index (not its name).
Generating multiple biomes at once
If you want to generate more than a few biomes at once, you can do this more efficiently by using a slightly more complex but faster method. However, learning the above method teaches you the gist of what you need to know. I used this more complex method to generate the custom biomes seen in the article’s first image. In total, I used 84 custom biomes, which generated smoothly on my relatively modest laptop. To do this, I recommend using Google Sheets, making columns for each parameter this complex method uses. Google Sheets can save you a lot of time, but it isn’t required. Nonetheless, I will make mine available for reference or derivative use.
This Google Sheet saves a great deal of time because it automatically places the parameters of the relevant function in list item format. This automated process may not sound like that big of a deal, but manually doing that for all 84 biomes is demotivating. With the use of the Sheet, I can copy and paste the column of “list code” within a list declared in Javascript instantly. This time-saving method will probably make more sense later on.
Generating biomes from a list
First things first, figure out how many biomes you want to generate (note that some biomes may not generate if no cells on the map fit their parameters). When you’ve determined the number, add that number by 12. For example, if I want to generate up to 84 custom biomes, I need to have a total of 96 biomes present in the Biomes Editor. Without these biomes already present, the generation code will not work as it requires indices to exist before assigning one to a cell’s biome data.
function generate_custom_biome(index, min_height, max_height, min_temp, max_temp, min_prec, max_prec) {
var biome_count = 0;
for (const i of pack.cells.i) {
if (pack.cells.h[i] >= min_height && pack.cells.h[i] <= max_height && grid.cells.temp[pack.cells.g[i]] >= min_temp && grid.cells.temp[pack.cells.g[i]] <= max_temp && (grid.cells.prec[pack.cells.g[i]] * 100) >= min_prec && (grid.cells.prec[pack.cells.g[i]] * 100) <= max_prec) {
pack.cells.biome[i] = index;
biome_count = biome_count + 1;
}
}
return biome_count;
}function generate_custom_biome_list() {
var index = 13;
for (const i in custom_biome_list) {
const min_height = custom_biome_list[i][3];
const max_height = custom_biome_list[i][4];
const min_temp = custom_biome_list[i][5];
const max_temp = custom_biome_list[i][6];
const min_prec = custom_biome_list[i][7];
const max_prec = custom_biome_list[i][8];
if (generate_custom_biome(index, min_height, max_height, min_temp, max_temp, min_prec, max_prec) > 0) {
biomesData.name[index] = custom_biome_list[i][0];
biomesData.color[index] = custom_biome_list[i][1];
biomesData.habitability[index] = custom_biome_list[i][2];
index = index + 1;
}
}
}
To generate biomes from a list, we’ll need two functions, as well as the list of biomes itself. Let’s first define the two functions. You’ll notice the first one is a modification of the function used above to generate a biome one at a time. The modified function returns how many cells the biome generates in. If the biome cannot generate in any cell, then the second function will not bother to update biomesData.
custom_biome_list = [
['Glaciers', '#f2f1ef', 0, 20, 69, -50, -1, 0, 9999],
['Polar deserts', '#f4eee1', 5, 20, 69, 0, 1, 0, 124],
['Subpolar deserts', '#f6ebd2', 10, 20, 69, 2, 3, 0, 124],
['Continental deserts', '#f7e9c4', 15, 20, 69, 4, 6, 0, 124],
['Oceanic deserts', '#f7e6b5', 20, 20, 69, 7, 12, 0, 124],
['Temperate deserts', '#f7e4a7', 25, 20, 69, 13, 17, 0, 124],
['Subtropical deserts', '#f6e199', 30, 20, 69, 18, 23, 0, 124],
['Tropical deserts', '#f4df8a', 25, 20, 69, 24, 30, 0, 124],
['Ultratropical deserts', '#f2dd7b', 20, 20, 69, 31, 50, 0, 124],
['Polar shrublands', '#dedacd', 40, 20, 69, 0, 1, 125, 9999],
['Subpolar shrublands', '#dfd7bf', 45, 20, 69, 2, 3, 125, 249],
['Continental shrublands', '#ded6b1', 50, 20, 69, 4, 6, 125, 249],
['Oceanic shrublands', '#ddd3a3', 55, 20, 69, 7, 12, 125, 249],
['Temperate shrublands', '#dcd195', 60, 20, 69, 13, 17, 125, 249],
['Subtropical shrublands', '#dacf87', 65, 20, 69, 18, 23, 125, 249],
['Tropical shrublands', '#d3ca76', 60, 20, 69, 24, 30, 125, 249],
['Ultratropical shrublands', '#cac664', 55, 20, 69, 31, 50, 125, 249],
['Subpolar grasslands', '#c8c4ac', 80, 20, 69, 2, 3, 250, 9999],
['Continental grasslands', '#c6c39f', 85, 20, 69, 4, 6, 250, 499],
['Oceanic grasslands', '#c4c191', 90, 20, 69, 7, 12, 250, 499],
['Temperate grasslands', '#c2bf84', 95, 20, 69, 13, 17, 250, 499],
['Subtropical grasslands', '#bfbd76', 100, 20, 69, 18, 23, 250, 499],
['Tropical grasslands', '#b2b663', 95, 20, 69, 24, 30, 250, 499],
['Ultratropical grasslands', '#a4af50', 90, 20, 69, 31, 50, 250, 499],
['Continental woodlands', '#afb08d', 80, 20, 69, 4, 6, 500, 9999],
['Oceanic woodlands', '#acae80', 85, 20, 69, 7, 12, 500, 999],
['Temperate woodlands', '#a8ad73', 90, 20, 69, 13, 17, 500, 999],
['Subtropical woodlands', '#a4ab66', 95, 20, 69, 18, 23, 500, 999],
['Tropical woodlands', '#92a252', 90, 20, 69, 24, 30, 500, 999],
['Ultratropical woodlands', '#7e983d', 85, 20, 69, 31, 50, 500, 999],
['Oceanic forests', '#949c70', 80, 20, 69, 7, 12, 1000, 9999],
['Temperate forests', '#8f9b64', 85, 20, 69, 13, 17, 1000, 1999],
['Subtropical forests', '#8a9a57', 90, 20, 69, 18, 23, 1000, 1999],
['Tropical forests', '#738e42', 85, 20, 69, 24, 30, 1000, 1999],
['Ultratropical forests', '#59812d', 80, 20, 69, 31, 50, 1000, 1999],
['Temperate rainforests', '#778955', 80, 20, 69, 13, 17, 2000, 9999],
['Subtropical rainforests', '#718849', 85, 20, 69, 18, 23, 2000, 3999],
['Tropical rainforests', '#547a33', 80, 20, 69, 24, 30, 2000, 3999],
['Ultratropical rainforests', '#346a1e', 75, 20, 69, 31, 50, 2000, 3999],
['Subtropical jungles', '#58773c', 80, 20, 69, 18, 23, 4000, 9999],
['Tropical jungles', '#366626', 75, 20, 69, 24, 30, 4000, 9999],
['Ultratropical jungles', '#005411', 70, 20, 69, 31, 50, 4000, 9999],
['Montane glaciers', '#fffefc', 0, 70, 100, -50, -1, 0, 9999],
['Montane polar deserts', '#fffdf0', 3, 70, 100, 0, 1, 0, 124],
['Montane subpolar deserts', '#fffbe3', 7, 70, 100, 2, 3, 0, 124],
['Montane continental deserts', '#fffbd7', 10, 70, 100, 4, 6, 0, 124],
['Montane oceanic deserts', '#fffaca', 13, 70, 100, 7, 12, 0, 124],
['Montane temperate deserts', '#fff9be', 17, 70, 100, 13, 17, 0, 124],
['Montane subtropical deserts', '#fff9b2', 20, 70, 100, 18, 23, 0, 124],
['Montane tropical deserts', '#fff9a5', 17, 70, 100, 24, 30, 0, 124],
['Montane ultratropical deserts', '#fff999', 13, 70, 100, 31, 50, 0, 124],
['Montane polar shrublands', '#f8f1dd', 27, 70, 100, 0, 1, 125, 9999],
['Montane subpolar shrublands', '#f5f0d1', 30, 70, 100, 2, 3, 125, 249],
['Montane continental shrublands', '#f3f0c5', 34, 70, 100, 4, 6, 125, 249],
['Montane oceanic shrublands', '#f2f0b9', 37, 70, 100, 7, 12, 125, 249],
['Montane temperate shrublands', '#f0efad', 40, 70, 100, 13, 17, 125, 249],
['Montane subtropical shrublands', '#efefa1', 44, 70, 100, 18, 23, 125, 249],
['Montane tropical shrublands', '#eaee92', 40, 70, 100, 24, 30, 125, 249],
['Montane ultratropical shrublands', '#e5ed83', 37, 70, 100, 31, 50, 125, 249],
['Montane subpolar grasslands', '#ebe6bf', 54, 70, 100, 2, 3, 250, 9999],
['Montane continental grasslands', '#e7e6b4', 57, 70, 100, 4, 6, 250, 499],
['Montane oceanic grasslands', '#e4e6a8', 60, 70, 100, 7, 12, 250, 499],
['Montane temperate grasslands', '#e1e69c', 64, 70, 100, 13, 17, 250, 499],
['Montane subtropical grasslands', '#dee691', 67, 70, 100, 18, 23, 250, 499],
['Montane tropical grasslands', '#d4e480', 64, 70, 100, 24, 30, 250, 499],
['Montane ultratropical grasslands', '#c9e170', 60, 70, 100, 31, 50, 250, 499],
['Montane continental woodlands', '#dbdca3', 54, 70, 100, 4, 6, 500, 9999],
['Montane oceanic woodlands', '#d5dc98', 57, 70, 100, 7, 12, 500, 999],
['Montane temperate woodlands', '#d0dd8d', 60, 70, 100, 13, 17, 500, 999],
['Montane subtropical woodlands', '#cbdd82', 64, 70, 100, 18, 23, 500, 999],
['Montane tropical woodlands', '#bcd96f', 60, 70, 100, 24, 30, 500, 999],
['Montane ultratropical woodlands', '#acd65e', 57, 70, 100, 31, 50, 500, 999],
['Montane oceanic forests', '#c6d388', 54, 70, 100, 7, 12, 1000, 9999],
['Montane temperate forests', '#bfd47d', 57, 70, 100, 13, 17, 1000, 1999],
['Montane subtropical forests', '#b8d473', 60, 70, 100, 18, 23, 1000, 1999],
['Montane tropical forests', '#a3cf60', 57, 70, 100, 24, 30, 1000, 1999],
['Montane ultratropical forests', '#8bca4e', 54, 70, 100, 31, 50, 1000, 1999],
['Montane temperate rainforests', '#adcb6f', 54, 70, 100, 13, 17, 2000, 9999],
['Montane subtropical rainforests', '#a4cc66', 57, 70, 100, 18, 23, 2000, 3999],
['Montane tropical rainforests', '#88c552', 54, 70, 100, 24, 30, 2000, 3999],
['Montane ultratropical rainforests', '#67bf41', 50, 70, 100, 31, 50, 2000, 3999],
['Montane subtropical jungles', '#8fc359', 54, 70, 100, 18, 23, 4000, 9999],
['Montane tropical jungles', '#6abb46', 50, 70, 100, 24, 30, 4000, 9999],
['Montane ultratropical jungles', '#36b336', 47, 70, 100, 31, 50, 4000, 9999]
];
The code above is what I use for my list of biomes to generate, but you can make yours however you like as long as it fits the formatting. Notice that you only add a comma at the end of a list item if another is below it. An example of how the formatting is structured is below.
['Biome name', 'Biome HEX colour code', habitability, min_height, max_height, min_temp, max_temp, min_prec, max_prec]
Once you have entered the two functions and the custom_biome_list in the Console, you can run the second function to generate the biomes in the list based on the parameters provided. To run the second function, enter what appears below in the Console. The second function has no parameters to provide within the round brackets.
generate_custom_biome_list()
After running the function, follow the steps described previously in the less complex method to refresh the Biomes Editor and display the generated biomes. Keep in mind; you only have to refresh the Habitability of one biome to trigger the recalculation of the others (at least from my testing). You may notice that some biomes are unused and still named “Custom,” this is to be expected if the second function could not find any suitable cells to assign those biomes to. Just delete them using the Biomes Editor (click on the trash can icon beside them).
Concluding thoughts
I know these methods are not exactly the most user-friendly or easy-to-use, but until a feature exists natively in the generator for achieving its results, this is the best it gets, or at least that I know. I once created and maintained a fork of the generator that expands the default biomes, but it still suffered from the same problem: the user cannot generate biomes other than the default ones I provided. It lacked customizability. I archived the fork since I could not keep up with its maintenance. There were other flaws with it, such as files saved with it could not be loaded in Azgaar’s generator version. Even if I did dedicate myself to maintaining it indefinitely, it would still suffer from cross-compatibility issues and not be updated as quickly as Azgaar’s version.
Credit
“Biome placement for fantasy worlds”, authored by mbartelsm, partially inspired my list of custom biomes.
Edit (August 8, 2024), typo:
- Removed broken link to my old fork of AFMG on GitHub. Added a link to the proper archive.