Making Framer Fast: Measuring Web App Performance
Creating a powerful tool means taking the time to consider performance impact in every decision.
A huge advantage from using the DOM for drawing is that it can do everything, from GPU accelerated graphics, to platform native font rendering, to advanced filters, to animations and videos. It’s a marvel of engineering to optimize for all of those at the same time. Plus it’s truly WYSIWYG, because you’re drawing it where others will see it. For example, under the hood Chrome actually uses Skia, which in its turn uses OpenGL (or even the modern even faster Vulkan or Metal backends), probably the fastest and most versatile graphics toolkit on the planet. If you are using Chrome, you can find your current graphics acceleration settings under chrome://gpu/.
When we say fast, what do we really mean?
When we talk about a fast canvas or fast computer in general, there are three parts that we mean. First, the time it takes things to load. For example, the time it takes for the app to load or for a specific page or document to load before you can see it and start working. Second, the time it takes to draw (every frame), determining how smooth scrolling and animations are. And finally, the time it takes something to respond to your actions, in other words, the latency or lag you feel—or don’t feel.
Just like the makers of any other tool, we use these measurements to assess Framer’s performance.
How we’re making Framer fast
Framer uses lazy loading or virtualization on the canvas. This means that it’s only drawing the actual layers that you can see, not the ones out of view. You can see this lazy loading at work if you scroll quickly or zoom out, the layers can take a second to appear. This is a very typical trick for computers. You’ve probably seen this using Google Maps for example, where it’s loading “tiles” if you scroll quickly to a new place. The browser actually does the exact same thing itself when you scroll too, but it’s so fast that you can’t see it. Even Tables on iOS do this. Or Google Sheets. Your refrigerator light, the double slit experiment, this trick is practically used everywhere.
Lazy loading in the Framer canvas
Additionally, Framer uses the graphics card (GPU) to draw (almost) everything. In CSS you can tell elements to draw on the GPU by using
will-change: transform. At that point, the element will basically be converted to a picture, like a jpg, and sent to your graphics card. The graphics card can manipulate those pictures “for free” but it can only do certain things: move, scale, rotate, skew, opacity, and order. But it’s so fast that it can do that for millions of pictures without breaking a sweat. This is why layers in Framer, or other tools, can get blurry for a split second when you zoom out. You probably wouldn't ever have noticed if I hadn't told you, so sorry about that.
While zooming, the GPU is in control, but it has to use the original image. After the zoom is done, it re-renders the image. Zooming in, funnily enough, doesn’t have the same effect as it needs less pixels, but it might have to render more elements in total that now fit on the visible canvas.
We measure a lot of performance metrics, but there are two very important ones, and they are surprisingly simple and effective.
- Loading time: every time a project is loaded we report the time it took to be ready for you to work on, including the project, size, browser, country, os, etc. From there we draw percentile graphs to see how our smallest, most common, and largest files take to load.
- Long frames: every time we “drop a frame” (we can’t draw 60 things per second) we increment a counter that we report, again including all the information. We divide the total time dropped by the total amount of time spent across all users in the app and we have a simple metric that lets us measure the real user experience.
As an example (and for full transparency), these are the real results for Framer showcasing the improvements we have been able to make over the last three months. We continuously monitor these per os, browser, and even for important specific enterprise teams that depend on us every day.
Framer loading time (lower is better)
Framer canvas speed (lower is better)
We hope these insights can help you make your web application faster too. From here, we'll highlight some specific things we did in Framer that really helped.
Performance highlights from the last 6 months
Optimized canvas drawing
We recently lowered memory usage of bigger projects by 25%, contributing to an even smoother panning and zooming experience, by creating a “static mode” for Framer Motion on the canvas.
Framer Motion is our open source and production-ready motion library for React on the web. Under the hood, it powers everything you create in the Framer canvas, including your Magic Motion animations.
When you’re designing on the canvas, it’s important that animations aren’t rendering while you’re working, as that would affect the editing performance. To do this, we use Framer Motion in “static mode”. By using a <MotionConfig> we can load just minimal parts we need from Motion for the rendering, without creating any performance overhead. This has great implications for your canvas but also the library itself.
“Framer Motion 4.0 allows animations and gestures to be lazy loaded, which reduces the bundle size of the minimal m component to a mere 5kb.”
This gives Framer Motion one of the smallest bundle sizes for an animation library of its nature.
Using optimal image size
In a recent release, we exposed full control over the image size we use to majorly improve canvas performance with image-heavy projects. This allows you to simply drop the images you want on the canvas, or pick one from Unsplash, and tweak it to the size you need.
Framer image fill with size control
The size of the images you use in Framer affects the performance as they require decoding (by either the GPU or CPU). However, when viewing your prototype, we use a larger size, up to 4096ｘ4096, in case you want to animate and scale up the image. So now, Framer will automatically pick the best size for you, but when creating animations it gives you full control over the image quality while keeping animation performance smooth.
Text rendering refactor
In December we fully replaced our text renderer to significantly speed up rendering, as well as, made additional improvements to how fonts are loaded.
“Replacing our text renderer helped decrease document loading time of large documents by up to 50%.”
Text is a big part of design. With Framer you can create auto-sizing text elements, text boxes, as well as, add rich styling like font, color, spacing, decoration, and more to each individual character. Our text editor supports languages that are written right-to-left like Hebrew and Arabic, and languages that composite multiple characters into one like Korean and Chinese. For all this we use the power editor of DraftJS.
While DraftJS is great for editing, it is relatively slow in rendering. The slowness adds up when using many hundreds or thousands of text elements in a project. Because we use effective caching when rendering, this is not too bad while using a project once it’s fully loaded, but we found that the text rendering had a big impact on the loading time of documents.
Default components and assets as ES Modules
In our latest performance-driven project, we switched all Default Components to utilize our new code delivery system based on the ES Modules standard. Default Components are available from the Insert Menu in every project and include everything from interactive buttons, inputs, and toggles to full icon sets and illustrations.
“With Default Components as modules we achieved adding code, in the form of a component, to a Framer project with zero install-time.”
The power of code is Framer’s not-so-secret weapon. While most features like Smart Components with transitions, animations, interactions, and visual states don’t actually require any code to use, certain components and packages rely on code behind the scenes.
Package loading time continues to decrease
This is just the start of where we are going with module support, so stay tuned for more features and improvements.
Great performance in the browser means continually working on projects that will target performance metrics and ensure a smooth experience in the canvas. Knowing which projects have been successful and made an impact, tells us exactly where to go next.
Want to read more about how we build Framer? Check out these posts: