Penpot's new rendering system
One of the hardest challenges in building a design tool like Penpot is rendering performance. As designs grow in complexity, ensuring smooth performance becomes increasingly difficult.
One of the hardest challenges in building a design tool like Penpot is rendering performance. As designs grow in complexity, with almost infinite possibilities, ensuring smooth performance becomes increasingly difficult.
A document can have infinite frames, shapes, effects, fonts, images, etc. but infinity is a concept that does not exist (computationally speaking), everything has a limit: memory, CPU time, GPU memory and time, etc.; the idea is to allow users to create without those in mind by creating an experience that feels limitless. And that's when certain techniques are key to smooth performance and offer that feeling of limitless potential, in this post we're going to cover a key new technique used by our new render engine: Tile rendering.
By the way, if you missed it, my colleague Elena Torró gave a wonderful talk about our new render engine in Penpot Fest 2025.
Tile rendering
Tile rendering is a very old concept that was mainly used to reduce the amount of memory needed to render huge scenarios in videogames using small reusable pieces called tiles. Modern browsers also use this technique to allow a extremely large amount of devices to be able to render web pages. GPUs also use this concept in a different way to allow rendering everything in parallel. In our case with a (in theory) limitless canvas, the idea is being able to deal with huge documents by rendering only those parts of the document that are visible and keep a cache of tiles that could be potentially visible.
In the following example, the magenta-filled area represents the document viewport, the surrounding magenta rectangle represents the potentially visible area, the yellow rectangles represent a series of randomly generated shapes, and the cyan rectangles represent the different categories of tiles. The filled ones are tiles that are rendered classified by their visibility; the brighter ones represent the tiles that intersect with the view, the slightly brighter ones are those that are in the potentially visible area, and the darker ones are those that are rendered and cached but not visible.
Visualization of how the tile rendering and tile cache works
A fixed amount of memory is reserved for tiles, so when this amount of cached tiles reaches its maximum capacity, what we do is start disposing those surfaces that are further from the view so we can guarantee that when the user moves around, at least we could keep those tiles that have a greater probability of being rendered.
In the next visualization, the parts inside tiles that are visible are rendered as white, while the ones that aren't visible but kept in cache are rendered as grey, those are kept in cache as long as possible.
Visualization of how rendering works
If you'd like to deep dive in the source code take a look at: https://github.com/penpot/penpot/blob/develop/render-wasm/src/tiles.rs
And if you want to join the conversation about our new render engine, join our Community: https://community.penpot.app/t/its-time-for-penpot-to-almost-move-away-from-the-dom/6437/37