skip to content

Impeller Renderer

Flutter's engine renders through Impeller, which precompiles shaders at engine build time, ending first-run shader-compilation jank. Interviewers ask what changed from Skia and where Skia remains.

part ofFlutteroverview, primer and where to startread it →
on this pageshow

explore

questions

5

What is Flutter's Impeller renderer, and what problem made it replace Skia as the default on iOS and Android?

level: middleimportance: must knowfreq 55%

answer

  1. shader compilation jank
  2. compiled at engine build time
  3. pipeline state built upfront
  4. iOS 3.10, Android 3.27
  5. desktop default in 3.47

basics

~20 s

Impeller is the engine's rendering runtime. Skia compiled shaders just in time, so an effect's first use could stall a frame; Impeller precompiles a smaller shader set when the engine is built and creates pipelines upfront, ending that first-run jank.

solid answer

~50 s

Impeller is the engine component that turns Flutter's layer tree into GPU work. With Skia, the shaders a scene needed were generated and compiled **just in time**, the first time a particular drawing combination appeared, which stalled that frame; the jank showed up on the first run of an animation or transition and vanished once shaders were cached. Warm-up schemes (generic warm-up, custom `ShaderWarmUp`, capturing SkSL in training runs) were fragile and device-specific. Impeller instead precompiles a smaller, simpler set of shaders **at engine build time**, builds its pipeline state objects upfront, and controls caching explicitly, so performance is predictable from the first frame. It became the default on iOS in 3.10 (now the only renderer there), on Android API 29+ in 3.27, and on macOS, Windows and Linux in 3.47. The web still renders with Skia.

go deeper

for a junior

Recall that Impeller is Flutter's renderer and that it exists to stop first-run animation stutter caused by compiling shaders at runtime.

for a middle

Explain just-in-time shader compilation, why warm-up schemes failed, and how precompiling at engine build time with upfront pipelines fixes it.

for a senior

Know the rollout per platform, where Skia still renders or is still linked, and separate renderer-caused jank from raster cost that Impeller does not remove.

for a principal

Weigh renderer maturity per platform when committing to a target, and set expectations that Impeller buys consistency rather than a blanket speed-up.

## What a renderer does in Flutter Flutter's framework does not draw pixels. Each frame it records drawing commands into a **layer tree** and hands the engine a `Scene`. The engine's **renderer** turns that scene into work for the GPU. For most of Flutter's history the renderer was **Skia**; today it is **Impeller** on most platforms. App code does not change between them: both consume the same scene, below `dart:ui`. ## The problem: shader compilation jank GPUs draw using **shaders**, small programs compiled for the specific GPU and driver. Skia generated shaders for the exact combination of effects a frame needed (a gradient under a clip with a blur, for example) and compiled them **just in time**, the first time that combination appeared. Compiling can take long enough to blow the frame budget, so the first time an animation or a page transition ran after install, it stuttered. On later runs the shader was cached and the same animation was smooth, which made the bug hard to reproduce on a developer's already-warm device. The workarounds each had a cost: - a **generic warm-up** that drew common shapes at startup, which could not predict an app's real shaders; - a custom **`ShaderWarmUp`** subclass assigned to `PaintingBinding.shaderWarmUp` (default `null`), hard to write correctly; - **training runs** that captured SkSL shaders and bundled them with the app, which were platform- and device-specific, grew the app, delayed launch and still missed flows nobody exercised. The tool's SkSL bundling was removed in the 3.32 cycle. ## Impeller's answer The Flutter docs list Impeller's objectives; the one that mattered most is **predictable performance**: 1. **Shaders are compiled offline.** Impeller uses a smaller, simpler set of shaders and compiles them, with reflection data, when the **engine** is built, using its own compiler (`impellerc`). Nothing about the built-in shaders is compiled on the user's device at runtime. 2. **Pipeline state objects are built upfront**, rather than on first use. 3. **The engine controls caching explicitly** instead of depending on driver heuristics. 4. It is **built for modern APIs** (Metal and Vulkan) while still supporting OpenGL ES. 5. It can **spread one frame's work across threads**, and it labels GPU resources so frames can be captured and inspected. The engine FAQ sums it up as "ahead-of-time (AOT) mode for rendering". ## Rollout | Release | Change | |---|---| | 3.10 | Impeller becomes the default on iOS | | 3.27 | Impeller becomes the default on Android API 29+ | | 3.47 | Impeller becomes the default on macOS, Windows and Linux | On iOS it is now the **only** renderer; the Skia path cannot be selected. On desktop the docs say the opt-out will be removed in a future release. The **web** still renders with Skia. ## What Impeller does not change - **Your widget code.** The framework, `dart:ui` and the layer tree are the same. - **Every cause of slow frames.** Expensive effects such as heavy `saveLayer` use still cost raster time under Impeller; that diagnosis belongs to performance work, not to renderer choice. - **All of Skia.** Impeller has no Skia graphics context, but text layout and shaping still use SkParagraph, and image decoding uses codecs wrapped by Skia. ## Worst frames, not just average frames Frame smoothness is judged by the **worst** frames, because a single 100 ms frame during an animation is visible even when the average is 8 ms. Just-in-time shader compilation hurt precisely those worst frames, and only on first use, which is also when a user forms an impression of an app. The engine FAQ reports that Impeller now does better than the old renderer in worst-frame benchmarks and is also faster on average; the design goal, though, was predictability. That is why interviewers expect the answer to lead with consistency rather than raw speed. ## How to talk about it in an interview - Name the mechanism: just-in-time shader compilation stalled first-run frames. - Name the fix: offline shader compilation at engine build time plus upfront pipelines. - Give the platform picture: iOS only Impeller, Android API 29+ default, desktop default since 3.47, web on Skia. - Avoid claiming Impeller makes every app faster; the documented win is consistency, especially in worst-case frames.

  • Why did shader-compilation jank so often escape testing before Impeller?
    It only happened the first time a given effect was drawn on a device with an empty shader cache. Developers ran the same screens repeatedly, so their devices were warm and the animation looked smooth. Fresh installs on users' devices hit the compile, which made it look like a random, unreproducible stutter.
  • Does Impeller remove Skia from a Flutter app entirely?
    No. When Impeller renders, Flutter creates no Skia graphics context, but text layout and shaping still go through SkParagraph, which is part of Skia, and image decoding uses codecs wrapped by Skia. The web renderers are also Skia-based, and Android devices below API 29 still render with Skia on OpenGL ES.
  • Is a custom ShaderWarmUp still useful in a Flutter 3.47 app?
    Rarely. `PaintingBinding.shaderWarmUp` defaults to `null`, and Impeller does not compile its built-in shaders on the device, so warming them up has nothing to do. It can only matter on a path where Skia still renders, and even there it costs startup time.

Skia was like a kitchen that mixed each sauce only when the first order for it arrived, so the first diner of the night waited; Impeller preps a fixed set of sauces before the restaurant opens, so the first order is served as fast as the hundredth.

saying these in an interview costs you the question

  • Impeller compiles shaders on the device during the first launch.
  • Impeller required apps to rewrite their widget painting code.
  • Impeller is the default renderer for Flutter web.
  • Skia shader jank was caused by slow Dart build methods.
  • Impeller makes every expensive effect free to render.
  • Impeller means Flutter no longer uses any part of Skia.
open as a page

In Flutter's architecture, what do the framework, the engine and the platform embedder each do, and how does a frame cross them?

level: juniorimportance: should knowfreq 55%

basics

~20 s

The framework (Dart) turns widgets into a layer tree; the engine (C++) runs Dart, exposes dart:ui and rasterizes scenes with Impeller; the platform embedder hosts the engine in a native app, providing the surface, threads, input and lifecycle.

open as a page

In Flutter 3.47, which graphics backend does the renderer use on each platform, and where does Skia still render?

level: middleimportance: should knowfreq 35%

basics

~10 s

iOS and macOS: Impeller on Metal. Android API 29+: Impeller on Vulkan, else Impeller's OpenGL ES backend; below API 29, Skia on OpenGL ES. Windows and Linux: Impeller since 3.47, over OpenGL. Web: Skia.

open as a page

Users of a Flutter meditation app report that the breathing animation stutters only the first time after install; how do you decide whether Impeller should have prevented it?

level: seniorimportance: should knowfreq 35%

basics

~20 s

First-run-only stutter is the signature of just-in-time shader compilation, which Impeller removes. So check which renderer the affected devices use (Android below API 29 still runs Skia); on Impeller devices, hunt other one-time costs such as image decoding.

open as a page

How do you temporarily switch a Flutter app off Impeller to check a rendering bug, and why is that only a short-term measure?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

Use flutter run --no-enable-impeller for a debug session, or the per-platform build switch such as Android's EnableImpeller manifest entry. iOS has no opt-out and the others are slated for removal, so the switch only isolates the bug.

open as a page