Building Figma Slides with Noah Finer and Jonathan Kaufman

The Pragmatic Engineer 58min 6 min #30
Building Figma Slides with Noah Finer and Jonathan Kaufman
Watch on YouTube

Summary

  • Figma Slides launched in April 2024, just over a year after it started as a hackathon project, and has already seen users create more than 4.5 million slide decks. Two engineers who worked on it from the beginning, Jonathan Kaufman and Noah Finer, walk through how the product was built, the unique technical challenges involved, and the engineering practices that set Figma apart.

How Figma Slides works and why it’s different

  • Figma Slides offers two views of the same file:
    • Single slide view – the familiar carousel-style interface (like Google Slides or PowerPoint) with a slide on the right and a thumbnail strip on the left.
    • Grid view – an infinite canvas where slides are laid out in rows and columns, matching how designers already work inside Figma Design.
  • The two views are linked: each row in grid view corresponds to a section in single slide view, and reordering in one view is reflected in the other. This two-way navigation was one of the first things the team built and is a core differentiator.
  • The team deliberately built grid view first because it was the riskier, more novel interaction. Single slide view was deferred for six or seven months because the team felt more confident they could build it later.

The tech stack: C++, WebAssembly, TypeScript, and React

  • Figma Slides runs on the same stack as Figma Design and FigJam:
    • The canvas itself is rendered by a C++ codebase (called “fullscreen”) compiled to WebAssembly, writing to a <canvas> element via WebGL or WebGPU. This ensures pixel-perfect rendering across browsers and operating systems.
    • All UI chrome – panels, toolbars, menus, the carousel – is built in TypeScript and React.
    • A bindings layer shares state between the C++ canvas and the web-based UI.
  • Engineers on the team tend to be split between C++ and web expertise, and Figma is progressively rewriting parts of the fullscreen C++ codebase into TypeScript that communicates with the C++ renderer through the bindings layer.
  • A notable example: the blue ”+” buttons and insertion indicators you see hovering over the grid are written in TypeScript/React but interact with the C++-rendered canvas through this bridge.

Debugging across C++ and JavaScript simultaneously

  • Figma engineers use a Chrome DevTools extension called DWARF/Wasm debugging (built into Chromium) to debug C++ WebAssembly alongside JavaScript in the same debugging session.
    • It works via source maps: a special debug build generates DWARF symbols, and the extension maps those to local source files.
    • Engineers can set breakpoints in both C++ and TypeScript, inspect variables, and step through code across both runtimes at the same time.
  • Noah used this to track down a race condition where a user was simultaneously in both grid view and single slide view – a bug caused by initialization code in C++ and a UI configuration flag in JavaScript firing in an unexpected order.

Building on the Figma platform: multiplayer and interop

  • A key architectural decision was to make single slide view just a viewport manipulation – toggling slides snaps the camera to a specific XY coordinate on the infinite canvas, rather than building a separate rendering mode.
    • This means multiplayer cursors, copy-paste, and all existing Figma tooling work in both views without extra engineering.
  • Multiplayer is powered by a Rust service that all clients connect to via WebSocket. Every edit propagates through this service, which handles conflict resolution and broadcasts changes. The C++ library handles this automatically – local edits must explicitly opt out of being shared.
  • Interop between Figma products (Design, FigJam, Slides) is a core philosophy:
    • Anything copied from Figma Design pastes seamlessly into Slides and vice versa.
    • This means every new node type (like FigJam’s stickies) must work across all editors, which adds significant complexity.
    • Most shared canvas logic lives in the same C++ WebAssembly bundle; differences between editors are expressed through mouse behaviors – a priority-list pattern borrowed from game engines where the first behavior to accept an input event handles it.
    • Example: dragging objects inside an auto-layout container in Figma Design shows real-time shuffling previews, while Slides has its own drag behavior with row previews and the ability to add new rows.

The rendering tradeoff

  • Figma renders all text itself – computing glyph geometry from font files and rasterizing pixels – rather than relying on the browser. This guarantees identical appearance across platforms, which is critical for a design tool.
    • The downside is that features browsers provide for free must be built from scratch: spell check (red squiggly underlines) and bidirectional text support were both major engineering undertakings.

The hardest problem: single slide view reordering with multiplayer

  • Single slide view’s carousel is deceptively complex because:
    • Collapse/expand state is local-only (not saved to the file), so the carousel must merge ephemeral web state with persistent C++ editor state.
    • Reordering slides in the carousel must be translated into mutations on the underlying grid, and those mutations must be minimized for multiplayer safety.
  • Early on, reordering one slide mutated every slide’s XY coordinates, which caused chaos when multiple users were editing or when someone went offline and came back (conflict resolution would produce incorrect results).
  • The solution: a two-level hierarchy where each slide lives inside a slide row node, and all rows live inside a slide grid node. Each slide has a parent index (a float from 0 to 1 referencing its position within its parent). Reordering now changes only the moved slides’ parent indices.
    • A reconciliation layer (similar in concept to React’s reconciler) computes the minimum number of mutations between drag start and end states.
    • This approach means reordering a single slide produces exactly one mutation, regardless of deck size.

Testing approach

  • The grid reconciliation code is the most unit-tested part of Slides, with every historically reported bug codified as a test case that verifies the exact mutations produced.
  • Beyond automated tests, Figma relies heavily on qualitative feel testing:
    • Engineers send designers two prototype variants (e.g., different zoom behaviors or margin sizes in single slide view) and gather preference feedback.
    • Internal dogfooding is central – Figma employees use the products daily as part of their actual work, so subtle bugs and inconsistencies get reported organically.
  • Figma maintains a zero bug policy for launched features: bugs get fixed immediately rather than backlogged. During the beta period for Slides, the team had more leeway to prioritize the end-to-end flow over edge-case bugs, but were disciplined about identifying true blockers rather than simply working longer hours.

Feature flags and the “run tests twice” practice

  • Figma has over 2,000 feature flags. For core editor changes and risky bug fixes, flags are always used and rolled out gradually.
  • A practice Figma adopted recently: all unit tests and interaction tests run twice – once with every feature flag off, then once with every flag on.
    • This catches regressions where code written under one flag breaks when another flag is enabled.
    • Example: Noah wrote a test assuming slides were named “Slide 1,” “Slide 2,” etc., but Jonathan’s slide numbers feature flag renamed them to just “1,” “2.” Running tests with all flags on caught the conflict before merge.
    • Getting to this point required significant infrastructure work: each flag needed configuration for “always on,” “always off,” and “on only in rollout tests,” and the team had to achieve green builds in both configurations.

Eng crits and internal tooling

  • Eng crits are Figma’s version of design/architecture reviews:
    • Instead of a long document, an engineer creates a FigJam file with a prototype and proposed code changes.
    • For 20 minutes, engineers from across the company jump in and leave sticky notes with feedback, creating threaded discussions.
    • The last 10 minutes are spent discussing the most contentious threads as a group.
    • This format lowers the barrier to giving feedback compared to formal document reviews.
  • Web bisect is an internal tool (built during a “maker week”) that uses frontend commit previews to binary-search for which commit introduced a regression:
    • A user reports a bug and roughly when they first saw it.
    • The tool picks a commit preview from the middle of that range; the user tests it and reports whether the bug is present.
    • After ~10 iterations, the exact offending commit is identified out of thousands.

Team structure

  • At its peak, the Slides team had 10–12 engineers, 2 designers, a dedicated user researcher, and a dedicated UX writer. There were no dedicated QA engineers – testing is owned by the engineering team.
  • The dedicated researcher was instrumental in testing prototypes with both designers and non-designers, helping the team understand how each audience interacted with the product differently.
Back to The Pragmatic Engineer