Mapgen4 renderer

 from Red Blob Games’s Blog
Blog post: 29 Sep 2025

It’s been a while since I’ve worked on mapgen4 features. In 2018 I declared it finished[1]. I then put it away and worked on other projects. Over the years I have updated the code (es6 modules, typescript, pointer events, boundary points, pnpm) without adding any features. I had been leaving all feature updates for a future map generator.

Last year I decided it was time to start thinking about new features. I started experimenting with some prototypes. That made me realize there are many improvements I’d like to make to mapgen4 before getting into big features.

Mapgen4 debug view

I made a list. One of the projects on the list is rewriting the renderer. But why?

  1. Migrate to WebGL2. I started mapgen4 in 2017. WebGL2 wasn’t widely available until 2021[2]. I’d like to use it both for features[3] and performance. I’m using a WebGL1 library, Regl.js[4], which does not support WebGL2[5].
  2. Reduce load time. Regl.js almost doubles the JS size of mapgen4. I would be able to shrink mapgen4 significantly by switching away from it.

Rewriting the renderer was not something I was looking forward to. It’s work that has to be done in “one shot”, where the intermediate steps aren’t testable. I decided to use an LLM to help me. I rarely use LLMs for coding and learned a lot.

  1. In this project I used the LLM for translating existing working code from Regl.js to WebGL1. This felt different from having it write new code.
  2. The resulting code had some subtle bugs. I was able to track some of them down by comparing the output of the original mapgen4 renderer with the rewritten renderer. Having a previous working version was important.
  3. It got me out of my analysis paralysis. Having something that runs rather than a blank editor window was a huge win.
  4. I didn’t like the structure of the code it generated. It triggered XKCD 386[6] for me. I ended up replacing all of its code, bit by bit, testing after each change.

Along the way I re-evaluated some of how the renderer worked. For example, I’m using nearest neighbor filtering in many places, even though linear might be better.

GL_NEAREST vs GL_LINEAR filtering

Look at how much smoother the ocean colors are with linear filtering! But there are blue splotches near rivers. I decided to keep nearest neighbor filtering for now.

I also discovered some bugs, like this one where one edge of the map is jagged, but only noticeable when the camera is rotated and zoomed in:

Bug: jagged edge

Overall, I don’t think the LLM saved me any time. I didn’t end up keeping the code it gave me. But it got me unstuck, and that meant I actually made progress.

The main benefit of the rewrite was size:

File Size before Size after
_worker.js 18543 18579
_bundle.js 150672 69469
(total) 169215 88048

The secondary benefits are that I got to revisit some of the decisions I made, I found some bugs, and I got unstuck. I didn’t stop at the main renderer. I also rewrote the river renderer, which saved another 8666 bytes. That’s for the next blog post.

Email me , or comment here: