Webassembly

19 posts

cloudflare3 min readCurated summary

Introducing Kitesurf: The agent-first browser that runs in V8 isolates on Cloudflare Workers

Cloudflare argues that AI agents need a browser optimized for machine tasks rather than human browsing. Chromium provides far more functionality than agents require while consuming too much memory and compute, limiting accessibility and scalability. The company therefore built Kitesurf, a lightweight browser running entirely on Workers and designed for agentic workloads. ## Why Cloudflare Built a New Browser - Cloudflare had repeatedly considered building a browser but previously found the technical investment difficult to justify. - Recent advances in its Developer Platform changed the equation: - Mature WebAssembly support in Workers - Dynamic workers - SQLite-based Durable Objects - Worker-to-worker RPC and service bindings - Improved Node.js compatibility and higher platform limits - Growing demand for AI browser automation exposed Chromium’s limitations: - High CPU and memory consumption - Expensive dedicated browser instances - Poor scalability for large numbers of agents ## Designing for Agents Instead of Humans - Agents prioritize: - Low token counts - Large context windows - Scalability and performance - Low operating costs - Structured, machine-readable content - They do not need many human-oriented features, such as: - Tabs, themes, extensions, and device synchronization - Pixel-perfect rendering - Smooth 60-frame-per-second scrolling - AI browser security requires a different threat model, with prompt injection and tool safety treated as central concerns. - Kitesurf became the result: a browser available in beta through Cloudflare’s Browser Run product. ## From Prototype to Product - The project began with inspiration from Obscura, a lightweight Rust headless engine for AI automation. - Cloudflare used an AI agent to attempt a port to Workers. - The first prototype was weak, but a detailed plan and explicit success criteria allowed the agent to iterate effectively. - The promising proof of concept led the team to develop Kitesurf further. ## Testing as a Foundation - Cloudflare relied heavily on automated testing to accelerate development without sacrificing quality. - Web Platform Tests (WPT) provided standards-based criteria for implementing browser features. - Engineers curated feature assignments and sequencing so AI agents could work toward measurable goals. - Because WPT does not fully capture real-world website behavior, Cloudflare added: - Multistep Puppeteer integration tests - Comparisons against Chromium - Visual regression checks at every interaction step - This combination tested both standards conformance and practical rendering behavior. ## Rust and WebAssembly - Kitesurf uses Rust wherever possible and compiles directly to WebAssembly with `wasm-bindgen`. - This avoids the bulk and performance costs associated with Emscripten’s emulation layers and mocked dependencies. - The approach allows browser components to run closer to native performance inside Workers. ## Resilience Through Exception Handling - Since browsers must process unreliable and potentially hostile web content, failures must not terminate entire sessions. - Kitesurf follows a strict rule: - Errors degrade to a blank frame or missing element - Faults are caught at component boundaries - Safe empty defaults are used - Diagnostic information is logged - This makes individual rendering failures survivable rather than allowing malformed input to crash the browser. ## Isolation and Statelessness - Every page load is treated as untrusted input. - Sessions begin fresh, and components receive only the resources they require. - Workers provide isolation boundaries, but Kitesurf also enforces isolation within the application itself to prevent data leakage between pages. - Components are kept stateless wherever possible: - Failed components can simply be recreated - Work can be scaled horizontally and run in parallel - Burst-based workloads avoid the cost of maintaining idle instances - Recovery can consist of restarting a component and replaying a request Kitesurf’s central recommendation is to build browsers around the needs of their users—in this case, AI agents. By sacrificing human-focused features and emphasizing efficiency, structured output, isolation, resilience, and scale, Cloudflare aims to make browser automation practical for a much broader range of agentic applications.

Read original(opens in new tab)
cloudflare2 min readCurated summary

Workers RPC now works across Python and JavaScript

Workers RPC, originally based on Cap’n Proto RPC, is expanding from JavaScript-only communication to seamless JavaScript–Python interoperability through Cap’n Web. Workers can call methods, pass objects and functions, propagate exceptions, and use native language types without schemas, dependencies, or significant performance overhead. The result is a multi-language system that can be used much like a local library. ## Cross-Language RPC in Workers - JavaScript Workers can call Python Worker methods, and Python Workers can call TypeScript methods. - Objects, functions, streams, and live remote objects can be passed between Workers. - A Service binding is the only required configuration. - RPC calls return promises in JavaScript/TypeScript and futures in Python. - Exceptions propagate back to the call site. - Most calls run in the same thread, providing near-zero overhead compared with local execution. - The implementation is open source through `workerd` and `workers-runtime-sdk`. ## Automatic Type Conversion - RPC supports Structured Cloneable values as parameters and return values. - Common types are converted into native equivalents, such as JavaScript `Date` to Python `datetime`. - JavaScript objects can correspond to Python dictionaries, while Python keyword arguments can represent JavaScript options objects. - Functions can cross the language boundary; invoking a transferred function creates a reverse RPC call to its original Worker. ## Pyodide’s Role - Python Workers use Pyodide, a WebAssembly-compiled CPython runtime. - Pyodide’s Foreign Function Interface translates common values automatically: - Python `int` and `float` → JavaScript `Number` - Python `bool` → JavaScript `Boolean` - Python `dict` → JavaScript `Object` - Python `list` → JavaScript `Array` - Types that cannot be directly converted, such as custom classes and functions, are represented by proxies that forward property access and method calls. ## Handling Worker-Specific Objects - Standard Web API objects such as `Request`, `Response`, `Blob`, and `File` do not have direct Python equivalents. - Pyodide initially exposes these values as JavaScript proxy objects. - Although proxies remain functional, they expose JavaScript implementation details to Python developers and make the API less natural. - The project therefore requires an additional conversion layer to provide Python-friendly representations of Cloudflare Workers objects. ## Practical Implication Cross-language Workers RPC lets teams combine Python and JavaScript services without manually designing APIs or serialization formats. Developers can use each language’s native calling conventions while the runtime handles translation, proxies, and communication behind the scenes.

Read original(opens in new tab)
cloudflare3 min readCurated summary

Making Rust Workers reliable: panic and abort recovery in wasm‑bindgen

Rust Workers historically treated Rust panics and aborts as fatal WebAssembly failures, potentially poisoning a Worker instance and causing unrelated requests to fail. Cloudflare’s latest work upstreamed into `wasm-bindgen` adds comprehensive recovery: `panic=unwind` preserves application state after recoverable panics, while abort handling ensures Rust code cannot run again after an unrecoverable abort. ## Initial Recovery Mitigations - Early Rust Workers used a custom panic handler to track failures and reinitialize the entire application before serving later requests. - JavaScript bindings were wrapped with Proxy-based indirection so every Rust entry point passed through recovery logic. - Generated bindings were modified to reinitialize the WebAssembly module after failures. - This approach shipped by default in `workers-rs` 0.6 and prevented persistent failure modes, but reinitialization could discard in-memory state. ## Panic Unwinding with WebAssembly Exception Handling - WebAssembly’s `wasm32-unknown-unknown` target traditionally defaults to `panic=abort`, turning panics into traps and `WebAssembly.RuntimeError` exceptions. - With WebAssembly Exception Handling support, Rust can be compiled using: ```bash RUSTFLAGS='-Cpanic=unwind' cargo build -Zbuild-std ``` - Unwinding allows Rust destructors to run, preserving state and cleaning up resources instead of terminating the entire instance. - `std::panic::catch_unwind` can translate a Rust panic into a recoverable `Result`. ## Changes to wasm-bindgen - The Walrus WebAssembly parser was updated to understand `try`/`catch` exception-handling instructions. - The descriptor interpreter was updated to evaluate code containing exception blocks. - Generated exports now catch Rust panics at the Rust–JavaScript boundary and expose them as `PanicError` exceptions. - Async exports reject their JavaScript promises with `PanicError`. - Exported functions use `extern "C-unwind"` so unwinding is explicitly permitted across the boundary. - A `MaybeUnwindSafe` trait checks `UnwindSafe` requirements only when compiling with `panic=unwind`. - For closures that cannot safely unwind, `Closure::new_aborting` provides an explicit alternative that terminates on panic rather than risking invalid state. ## Results of `panic=unwind` - Panics in exported Rust functions are caught by `wasm-bindgen`. - JavaScript receives a `PanicError`. - Async calls reject their promises instead of poisoning the Worker. - Rust destructors execute correctly. - The WebAssembly instance remains valid and reusable. - Stateful applications, including Durable Objects, can recover without losing all in-memory state. ## Abort Recovery - `panic=unwind` cannot handle aborts such as out-of-memory failures because aborts do not unwind. - The remaining recovery mechanism prevents Rust code from being re-entered after an abort, avoiding repeated execution in a corrupted WebAssembly state. - Together, unwinding and abort recovery prevent one failed request from poisoning sibling or future requests. The recommended approach is to use the latest `wasm-bindgen` and Rust Workers releases, enabling `panic=unwind` where state preservation matters while using explicit aborting closures when unwind safety cannot be guaranteed.

Read original(opens in new tab)
discord3 min readCurated summary

Bringing DAVE to All Discord Platforms

Discord is making DAVE, its end-to-end encryption protocol for audio and video calls, mandatory across all platforms. Browser support required solving WebRTC compatibility issues, designing an efficient Web Worker architecture, and reusing proven C++ cryptography through WebAssembly. Clients without DAVE support will be unable to join calls starting March 1, 2026. ## DAVE Becomes the Standard - DAVE already protects tens of millions of Discord calls daily. - Support is expanding to browsers, consoles, and the Social SDK. - Non-DAVE clients and applications will lose access to Discord calls on March 1, 2026. ## Browser Support and Firefox Compatibility - Discord uses the WebRTC Encoded Transform API to encrypt audio and video inside the WebRTC pipeline. - Firefox initially failed during real calls because its encryption Web Worker received no media data. - Discord engineers identified a recursive mutex deadlock in Firefox’s `FrameTransformerProxy`, triggered when video arrived too early. - Mozilla merged Discord’s fix, which is available in Firefox 142.0—the minimum Firefox version required for DAVE. ## Web Workers and Call State - Dedicated Web Workers encrypt and decrypt media: - One worker handles call audio and camera video. - Separate workers handle screenshare and game-stream audio and video. - Each media stream has a unique SSRC, allowing workers to select the correct symmetric encryption key for each frame. - Workers retain only essential call state, including SSRC-to-user mappings and encryption keys. - The main thread manages WebRTC connections, participants, and media tracks. - MLS membership changes are also handled on the main thread, preventing encryption work from delaying users joining or leaving calls. - Cryptographic state changes are sent asynchronously to workers. ## WebAssembly for Proven Cryptography - Discord compiled its existing, battle-tested C++ DAVE implementation to WebAssembly. - Reusing the same implementation across platforms reduces platform-specific security and reliability risks. - DAVE must selectively encrypt media while preserving metadata needed by WebRTC packetization and depacketization. - Since encrypted output cannot be modified in transit, byte-level parsing must be precise. - WebAssembly provides near-native performance while avoiding a more error-prone JavaScript reimplementation. ## WebAssembly Versus Browser Cryptography APIs - WebAssembly introduces a small performance cost compared with native browser APIs such as `SubtleCrypto`. - Discord’s benchmarks evaluate this trade-off against the benefits of shared, mature cryptographic code. - The post indicates that WebAssembly remains practical because frame parsing and selective encryption are computationally complex, while the security and portability benefits outweigh the minor cryptographic overhead. Discord’s platform transition means developers maintaining Discord clients, integrations, or SDK-based applications should add DAVE support before March 1, 2026. Browser users must also use Firefox 142.0 or newer when connecting through Firefox.

Read original(opens in new tab)
datadog3 min readCurated summary

How we built a real-time, client-side noise suppression library without server dependencies

Datadog’s CoScreen team needed high-quality noise suppression that could run in real time on client devices and integrate with WebRTC. Since existing solutions were either too slow, server-dependent, expensive, or difficult to embed, they built and open-sourced **dtln-rs**, a portable Rust library based on the DTLN model. It processes one second of audio in about 33 ms on an M1 MacBook Pro and supports WebAssembly, Node.js, and native clients. ## Introducing dtln-rs - dtln-rs is a lightweight, open-source noise reduction library based on the Dual-Signal Transformation LSTM Network (DTLN). - It can produce: - A WebAssembly module - A native Rust library - A Node.js native module - The library is designed to integrate with WebRTC-based applications. - Datadog also released a demo showing how to embed the filter in an application or webpage. ## Demonstrating Real-World Noise Suppression - The project was motivated by common remote-work disruptions, including lawn mowers and other background noise. - In one test, the filter removed a neighbor’s lawn mower so effectively that a colleague could not tell it was running. - The team used this result as evidence that the embedded library could provide meaningful value to CoScreen users. ## How DTLN Enables Real-Time Processing - AI noise suppression learns to distinguish desired speech from unwanted background sounds. - DTLN uses a short-time Fourier transform (STFT) to divide audio into smaller segments and analyze the magnitude of different frequencies. - It also uses phase information, which describes the starting position of each frequency in the sound wave. - A model analyzes magnitude and phase data to determine which parts are speech and which are noise. - Its LSTM-based architecture can adapt to different environments, such as: - Air-conditioner hum - Cafe conversations - Paper rustling - The combination of deep learning and efficient signal processing allows DTLN to operate with near-instantaneous latency. ## Why Existing Noise Suppression Solutions Were Insufficient - Many advanced machine-learning models require powerful backend servers, with processed audio sent back over the network. - This approach adds latency, infrastructure complexity, and operating costs. - WebRTC remains widely adopted but generally relies on older, built-in noise reduction techniques. - Earlier solutions such as RNNoise can reduce noise but often do not match the quality of newer commercial systems. - Although Web Audio and WebAssembly make custom client-side processing possible, implementation still requires substantial engineering effort. - Large companies can deploy specialized servers and models trained on enormous speech datasets, but smaller teams may not have the resources to do so. - CoScreen’s search for an alternative led to DTLN, which could run in real time on standard hardware and be embedded directly into client applications. ## Practical Recommendation For WebRTC applications needing client-side, real-time noise suppression, dtln-rs offers a portable alternative to expensive server-based services. Its Rust foundation and support for WebAssembly, Node.js, and native targets make it suitable for web, desktop, and embedded clients.

Read original(opens in new tab)
figma3 min readCurated summary

Figma’s journey to TypeScript | Figma Blog

Figma migrated its custom Skew programming language to TypeScript after its original performance advantages became less important than its maintenance and onboarding costs. Advances in mobile WebAssembly support, C++ engine integration, and team growth made the transition practical without sacrificing significant performance. The team completed the migration through an automated, gradual rollout that preserved development velocity and minimized production risk. ## Why Figma Moved Away from Skew - Skew originally helped Figma support prototype viewing across web and mobile. - Its compiler provided optimizations such as: - Constant folding - Devirtualization - Efficient JavaScript integer operations - Fast compile times - Over time, Skew became difficult to scale because: - New engineers struggled to learn it. - It integrated poorly with the broader codebase. - It lacked an external developer ecosystem. - Maintaining its specialized tooling outweighed its performance benefits. - TypeScript offered native package management, static imports, modern language features, extensive tooling, and easier hiring and onboarding. ## Why the Migration Became Possible - Mobile browsers gained broad WebAssembly support by 2018, with reliable performance by 2020. - Figma moved many performance-critical Skew components—especially hot paths such as file loading—to its C++ engine. - These C++ replacements reduced the performance penalty of moving less-critical code to TypeScript. - Larger prototyping and mobile teams provided enough capacity to invest in automated migration tooling. ## Addressing Performance Concerns - In 2020, early benchmarks showed TypeScript could make prototype loading nearly twice as slow in Safari. - Safari was especially important because WebKit was the only browser engine permitted on iOS at the time. - Improved WebAssembly support and the shift of core engine work to C++ made Skew’s compiler optimizations less essential. - Figma gained confidence that TypeScript could provide acceptable performance without recreating Skew’s custom compiler. ## Automated Skew-to-TypeScript Conversion - Manually rewriting the entire codebase would have disrupted development and increased the risk of runtime bugs and regressions. - Figma built a transpiler that converted Skew into TypeScript, extending earlier work by former CTO Evan Wallace. - The migration required care because Skew and TypeScript had different runtime semantics. - For example, TypeScript initializes namespaces and classes only after a module is imported, while Skew made symbols available globally when the codebase loaded. Unexpected import order could therefore introduce runtime failures. ## Three-Phase Rollout ### Phase 1: Write Skew, Build Skew - Figma kept the existing build process. - The new transpiler generated TypeScript from Skew. - Generated TypeScript was checked into GitHub so developers could inspect and prepare for the future codebase. ### Phase 2: Write Skew, Build TypeScript - Once the generated bundle passed unit tests, production traffic began using the TypeScript build. - Developers continued writing Skew. - The transpiler updated the TypeScript source automatically. - The team fixed type errors incrementally; TypeScript could still produce valid bundles despite those errors. ### Phase 3: Write TypeScript, Build TypeScript - After the team adopted the TypeScript build, the generated code became the source of truth. - Figma stopped automatic generation, deleted the Skew source, and required new development to use TypeScript. - The staged process allowed the team to detect and resolve issues such as a Smart Animate regression before completing the cutover. ## Practical Lessons - A custom language can provide valuable early advantages but become a long-term developer-experience liability. - Automated conversion is safer when paired with staged production rollouts and reversible adoption gates. - Controlling the original compiler made it possible to adapt the migration tooling to the codebase’s specific needs. - Figma’s approach demonstrates that large language migrations can preserve delivery speed when technical, performance, and organizational prerequisites are addressed first.

Read original(opens in new tab)
figma2 min readCurated summary

Server-side Sandboxing: An Introduction | Figma Blog

Server-side sandboxing helps contain the damage caused by vulnerabilities in software that processes untrusted user input. This is especially important for image processing, parsing, compression, and thumbnailing libraries often written in memory-unsafe languages, as demonstrated by ImageTragick. Figma argues that sandboxing complements—rather than replaces—secure coding by limiting a compromised workload’s access to data, services, and infrastructure. ## Why Server-Side Sandboxing Matters - Modern SaaS applications must process user-generated content using complex libraries. - Many of these libraries are written in C or C++, which are vulnerable to memory-corruption bugs. - ImageTragick showed how a vulnerability in ImageMagick could enable remote code execution when processing user-supplied images. - Preventing every vulnerability through rewrites, memory-safe languages, or program analysis is expensive and imperfect. - Sandboxing provides defense in depth by containing failures when vulnerabilities are exploited. ## Figma’s Server-Side Risk - Figma uses server-side components such as RenderServer, a C++ version of the editor, along with third-party libraries for graphical data. - Malicious input processed directly inside production infrastructure could allow an attacker to: - Access data belonging to other jobs - Make requests to internal production services - Move laterally through the environment - Compromise additional systems - Sandboxing reduces the external interfaces and resources available to potentially compromised workloads. ## Common Sandboxing Approaches - The article introduces three major sandboxing primitives: - **Virtual machines (VMs):** Isolate workloads through a hypervisor and separate guest operating systems. - **Containers:** Isolate workloads using operating-system-level mechanisms and container engines. - **Seccomp:** Restricts the system calls a program is permitted to make. - Each approach involves trade-offs in security properties, operational complexity, performance, and suitability for different workloads. - The article’s broader goal is to help teams compare these options and select an appropriate combination of isolation techniques. ## Choosing an Appropriate Strategy - Sandboxing technologies have historically been expensive, immature, or difficult to operate at scale. - Recent improvements have made virtualization, containment, and workload isolation more practical for a wider range of security teams. - Teams should evaluate sandboxing based on their workload’s risk, required interfaces, resource needs, and acceptable operational trade-offs. Teams should treat sandboxing as a practical layer of defense around risky processing workloads, rather than relying solely on preventing vulnerabilities.

Read original(opens in new tab)
figma2 min readCurated summary

Keeping Figma Fast | Figma Blog

Figma’s original performance-testing system relied on a single MacBook running a few repeated scenarios, which worked when the product and team were smaller. As Figma expanded into plugins, FigJam, Dev Mode, and many other features, that approach became too limited and fragile—the laptop eventually overheated while the company was remote. Figma concluded it needed a scalable, proactive testing framework capable of catching regressions across its growing codebase. ## From One Laptop to a Growing Product - In 2018, one MacBook repeatedly ran performance scenarios and reported timing changes to a shared dashboard. - A major renderer restructuring and WebAssembly fixes made Figma three times faster. - Over five years, the product grew substantially, adding: - Plugins and Community features - FigJam - Dev Mode - Numerous ongoing product updates - The existing test files could no longer represent the product’s expanding feature set and edge cases. ## Limits of the Original Testing Process - Granular performance tests measure specific behavior at scale, such as panning through a file while 100 users edit layers and type simultaneously. - Creating feature-specific tests became impractical as Figma grew beyond 400 engineers and managers. - Increasing release velocity made it difficult for any individual or single machine to track every performance-affecting change. - During the shift to remote work in 2020, the office MacBook remained running unattended and eventually overheated. - Attempts to reproduce the setup on another laptop were unsuccessful, demonstrating that the system was not operationally scalable. ## Requirements for a New System Figma used the overhaul to define an ideal performance-testing framework: - **Test every proposed code change:** Performance checks should run against changes in the main monorepo, allowing regressions to be found during development rather than after users encounter them. - **Support proactive performance work:** Small delays can significantly disrupt users who spend hours working in Figma. - **Run tests in parallel:** Dozens of stress scenarios would need to execute simultaneously, similar to Figma’s existing cloud-based CI testing. - **Finish quickly:** Performance guardrail checks were required to complete in under 10 minutes. - **Scale across real hardware:** Running every pull request on physical machines could require roughly 100 identical runners at peak capacity. Figma’s experience shows that performance testing must evolve alongside product complexity. A lightweight single-machine setup can be effective initially, but larger teams and faster release cycles require automated, parallel, hardware-aware testing integrated directly into CI.

Read original(opens in new tab)
figma3 min readCurated summary

Why Roles Are Not Rules | Figma Blog

Figma CTO Kris Rasmussen argues that engineering roles should guide collaboration, not restrict who participates in product decisions. As products become more collaborative and nonlinear, engineers must contribute not only to implementation but also to deciding what to build. However, collaboration works best when teams balance broad input with clear ownership, milestones, and momentum. ## Collaboration Beyond Traditional Roles - Modern product development is increasingly multiplayer, shaped by: - Web technologies such as WebGL and WebAssembly - Collaboration tools like Google Docs - Hybrid and remote work - Engineers are no longer simply responsible for executing plans created by product managers and designers. - At Figma, engineers help determine both: - **How** to build products - **What** to build - Cross-functional collaboration includes working closely with product peers and incorporating customer feedback. ## Early, Open Design Work - Teams are encouraged to gather diverse perspectives at the beginning of a project. - Engineers write down early ideas in concept documents rather than waiting until proposals are polished. - Feedback is collected during the drafting process, allowing designs to evolve collaboratively. - This approach helps expose problems earlier, though it can be difficult to obtain timely feedback from teams focused on their own work. ## Engineering Crits as Feedback Forums - Figma holds regular engineering “crits” across design and engineering organizations. - Crits provide: - Early and frequent feedback - Expert input on technical designs - A dedicated forum for cross-team participation - They are explicitly **not approval meetings**: - No decisions need to be finalized during the session. - Participants identify problems and improve designs without immediately choosing a solution. - Figma uses FigJam so participants can collaborate in real time. - The goal is to improve a proposal until it no longer requires formal approval. ## Balancing Input with Direction - Collaboration can become counterproductive when teams receive too many conflicting ideas. - Excessive feedback may cause projects to: - Lose focus - Get stuck in endless exploration - Struggle with ambiguous tradeoffs, such as defining an initial pricing model - Teams need a balance between: - Diverging to explore possibilities - Converging to make progress - The right balance depends on the organization, culture, product, and desired outcomes. ## Milestones and Momentum - Figma breaks projects into clearly defined milestones. - Milestones help: - Set expectations with stakeholders - Signal when exploration should give way to decisions - Preserve forward momentum - Momentum makes goals feel attainable, while losing momentum can cause teams to question their direction and spin in circles. The practical lesson is to treat roles as areas of responsibility rather than rigid boundaries. Invite collaboration early, use structured forums for feedback, and establish milestones that make it clear when the team must stop exploring and move forward.

Read original(opens in new tab)
figma2 min readCurated summary

How Figma Draws Inspiration From the Gaming World | Figma Blog

Figma’s technology shares more with a game engine than a traditional web application. Like games, it combines graphics rendering, interaction, multiplayer, animation, and other systems to create a responsive digital world. This architecture, along with close collaboration across engineering, design, product, and research, enables Figma and FigJam to support complex creative work in real time. ## Engineers as Digital World-Builders - Game engines combine foundational systems such as: - Graphics and rendering - User controls - Multiplayer - Physics and collisions - Animation - Artificial intelligence - Combat or other specialized mechanics - Figma similarly builds a 2D graphics and rendering system for the web. - Engineers ensure that text, shapes, and lines appear correctly while users pan and zoom across a canvas. - Collaboration is central to Figma, so its real-time collaboration engine is called “multiplayer,” inspired by cooperative games. - Figma and FigJam are built from many interacting “systems,” including: - Multiplayer editing - Spring animations - Audio and cursor chat - Component Variants - Plugins and widgets - Because these systems must run efficiently in browsers and mobile apps, Figma uses a game-engine-like stack rather than a conventional web stack. - The canvas is written in C++ and compiled to WebAssembly, helping address memory and performance constraints. ## Creativity Requires Systems-Level Collaboration - Game developers work closely with artists and designers to refine both technical behavior and user experience. - Figma follows a similar model, with engineers collaborating across product management, design, data science, and research. - Interdependent systems create emergent behavior: changing one system can affect many others. - The article compares this to *The Legend of Zelda: Breath of the Wild*, where fire can provide warmth and food, cause damage, or help defeat enemies. - Figma’s complexity produces comparable interactions, where an apparent problem in one feature may actually result from behavior elsewhere in the system. - This systems-level collaboration helps teams investigate unexpected failures and improve the overall product rather than treating each feature in isolation. Figma’s game-inspired approach is both architectural and cultural: build modular, interacting systems, optimize for real-time performance, and involve diverse disciplines in solving problems. Teams building similarly complex collaborative tools can benefit from treating the product as a living digital world rather than as a collection of disconnected web features.

Read original(opens in new tab)
figma2 min readCurated summary

Behind the feature: the hidden challenges of autosave | Figma Blog

Figma’s expanded autosave protects offline edits by persisting pending changes even if the browser tab closes. The feature was difficult because Figma combines large, mutable documents with browser performance limits and real-time multiplayer editing. Rather than repeatedly serializing entire files, Figma chose to store and later replay only the changes made while disconnected. ## Why Whole-File Autosave Was impractical - Figma documents are scenegraphs—trees of layers that can reach tens of megabytes compressed and hundreds of megabytes in memory. - Serializing a large document can take seconds; even an optimized 100 ms operation would cause noticeable stutters because JavaScript and WebAssembly are generally single-threaded. - Splitting serialization across browser frames could reduce blocking, but introduces consistency problems if users edit the document while it is being serialized. - Reading from an immutable scenegraph would solve consistency issues, but adopting immutable data structures would require a major rewrite and could increase memory usage and slow writes. - Replacing a cloud file with an offline backup could overwrite newer edits from collaborators. Keeping the backup as a separate copy would also be problematic for files that act as shared sources of truth, such as design-system component libraries. ## Saving Changes as a Delta - Figma already tracks unsent edits as “deltas” for its multiplayer editing system. - When a document goes offline: - User edits accumulate in an in-memory pending-changes buffer. - The buffer is periodically written to disk. - If the document closes, the changes remain available. - On reload, the changes are applied to the latest document version and uploaded to the server. - This approach avoids serializing the entire scenegraph and naturally preserves newer server-side changes. ## Browser Storage and Granularity - Figma uses IndexedDB because it supports: - Large amounts of browser-side data - Storage in smaller chunks - Database indexes - Transactional operations for data integrity - Pending changes are stored per file and per node or layer as property changes. - This granularity balances storage overhead against redundant disk I/O: finer-grained records reduce unnecessary writes but require more metadata and rows. Figma’s autosave demonstrates that reliable offline persistence is not simply a matter of writing files to disk. For large, collaborative applications, storing incremental changes provides better performance and safer reconciliation than saving and restoring complete document snapshots.

Read original(opens in new tab)
figma3 min readCurated summary

React at 60fps: improving scrolling comments in Figma | Figma Blog

Figma improved comment-scrolling performance threefold by targeting unnecessary React work during canvas panning and zooming. Although comment pins must recalculate their positions on every viewport update, unrelated fixed-position UI components were also re-rendering. By isolating viewport-dependent updates and optimizing comment transformations, Figma moved closer to its goal of maintaining 60fps even in files with many comments. ## The Performance Goal - Figma aimed to render the editor at 60fps, which is substantially smoother than 15 or 30fps. - Growing numbers of comments and threads exposed slowdowns while users panned and zoomed around the canvas. - Comment pins are anchored to canvas content, so their positions must continuously respond to viewport changes. ## Figma’s Rendering Architecture - The editor combines WebGL, WebAssembly, TypeScript, and React—effectively a browser-based design tool with a dynamic React interface. - Viewport updates are stored in Redux. - Comment components read viewport state and calculate their positions relative to the canvas. - Unlike static React interfaces, comments move as part of the canvas and must update frequently. ## Diagnosing the Bottleneck - Chrome Performance tools showed that JavaScript consumed most of the frame time. - With 30 comments, approximately 68ms per frame was spent on JavaScript, producing about 19fps. - React Profiler showed that rendering the comments themselves took only about 1.8ms. - The larger problem was unnecessary re-rendering of fixed UI elements such as: - The left panel - Toolbar - Properties panel - Comments list - Other components that did not depend on viewport movement - This revealed that viewport updates were propagating too broadly through the React component tree. ## Preventing Unnecessary Re-renders - Figma narrowed which components subscribed to viewport changes. - Components that did not need changing viewport data were prevented from re-rendering. - The optimization focused on separating dynamic canvas-attached comments from fixed interface elements. - Reducing this wasted React work freed time in each frame for the comment pins that actually needed updates. ## Optimizing Comment Positioning - Comment pins must transform their positions whenever the canvas viewport changes. - Figma optimized the transformation calculations and the way those updates were applied to the components. - This reduced the JavaScript cost of moving many comment pins simultaneously. ## Results - Scrolling comments became roughly three times faster. - The improvements brought performance closer to the 60fps target. - Figma planned to continue improving performance as files and comment counts scale. The main lesson is to profile both browser execution and React rendering separately. For highly interactive views, performance depends not only on optimizing the visible components, but also on ensuring that unrelated parts of the application do not re-render in response to high-frequency state updates.

Read original(opens in new tab)
figma2 min readCurated summary

An update on plugin security | Figma Blog

Figma disclosed vulnerabilities in the third-party Realms shim used to sandbox plugins. Although the vulnerabilities could have allowed plugins to escape their security boundaries, Figma found no evidence of exploitation after auditing published plugins. The company patched the issues, paused plugin publishing, and replaced the Realms shim with QuickJS running in WebAssembly. ## Plugin Security Boundaries Figma designed plugins so they can: - Run only after explicit user action. - Display UI in a plugin-specific dialog. - Read and modify data in the current Figma document. - Communicate with external internet services. Plugins should not be able to: - Run automatically or access data when inactive. - Obtain project or team information. - Read files other than the one in which they were launched. - Modify Figma’s interface outside their own dialog. Organization-tier administrators can also restrict plugin use through an allowlist. ## Response to the Vulnerabilities The Realms shim vulnerabilities could have allowed sandboxed code to bypass these restrictions. - Figma halted publication of new plugins and updates to existing plugins. - Existing plugin updates were disabled because live code changes propagate immediately to open clients. - Figma applied the publicly disclosed patch as soon as Agoric released it. - Privately disclosed vulnerabilities were fixed before the coordinated public disclosure. - Figma audited published plugins and found no evidence that the flaws had been exploited. Figma clarified that manual plugin review focuses primarily on user experience. Security is enforced through sandboxing rather than relying on human review, which can miss malicious behavior. ## Replacing the Realms Shim Figma permanently changed its plugin execution technology: - The Realms shim was removed entirely. - Plugins now run using QuickJS, a JavaScript virtual machine written in C and compiled to WebAssembly. - Figma’s architecture allowed the implementation to be swapped quickly because QuickJS had already been prepared as a backup. - The newly discovered Realms-specific vulnerability class no longer applies to the new implementation. Figma’s approach demonstrates the importance of defense-in-depth: sandboxing should enforce security boundaries, while rapid patching, controlled disclosure, plugin audits, and an interchangeable runtime architecture limit the impact of third-party vulnerabilities.

Read original(opens in new tab)
figma3 min readCurated summary

How to build a plugin system on the web and also sleep well at night | Figma Blog

Figma’s plugin system had to let untrusted third-party JavaScript interact with a powerful, browser-based design editor without compromising security, performance, or stability. The team evaluated several isolation strategies, ultimately favoring JavaScript Realms because they supported synchronous APIs and avoided the performance costs of a full interpreter. Figma later replaced that implementation with a JavaScript VM compiled to WebAssembly after a vulnerability was found in the third-party Realms shim. ## Why Plugin Isolation Was Difficult - Plugins needed access to Figma’s document model while remaining isolated from: - User data and credentials - Figma’s internal application state - Other plugins - The host page and browser APIs - Simply calling `eval(PLUGIN_CODE)` would execute arbitrary code in Figma’s main environment. - Figma’s architecture added constraints: - The editor relied heavily on WebGL and WebAssembly. - Parts of the interface used TypeScript and React. - Multiple users could edit files simultaneously. - Plugins also needed to remain performant and avoid breaking as Figma evolved. ## Attempt 1: The `<iframe>` Sandbox - The team first considered the browser’s standard isolation mechanism: sandboxed `<iframe>` elements. - An iframe could separate plugin code from Figma’s main page and restrict access using browser security policies. - Communication between the plugin and Figma would use mechanisms such as `postMessage`. - However, this created an important limitation: iframe communication is asynchronous. - Figma’s plugin API needed synchronous access to document operations, making an iframe-based architecture awkward and potentially expensive. - The iframe approach also introduced additional browser contexts and messaging overhead. ## Attempt 2: A JavaScript Interpreter Compiled to WebAssembly - The second approach was to run plugin code inside a JavaScript interpreter rather than the browser’s native JavaScript engine. - The interpreter could expose only explicitly approved Figma APIs, providing a strong security boundary. - Compiling the interpreter to WebAssembly offered a way to integrate it efficiently with Figma’s existing WebAssembly-heavy architecture. - The drawbacks included: - Interpreted JavaScript would be slower than native execution. - The interpreter would require ongoing maintenance and compatibility work. - Supporting the full JavaScript language and modern features would be difficult. - Although attractive from a security perspective, this approach appeared to impose too much performance and implementation cost at the time. ## Attempt 3: JavaScript Realms - Realms provided a separate JavaScript global environment within the same browser process. - Figma could execute plugin code in a distinct Realm while exposing a carefully controlled plugin API. - Unlike iframes, Realms allowed plugin calls to remain synchronous. - Unlike a custom interpreter, plugin code could use the browser’s native JavaScript engine. - The implementation required carefully controlling built-in objects and preventing plugins from escaping their isolated environment. - This approach offered the best balance of: - Native JavaScript performance - Synchronous API access - Isolation from Figma’s application state - A relatively small integration surface ## Later Security Change - After publication, Figma discovered a security vulnerability in the third-party Realms shim used by its original implementation. - The vulnerability was fixed before public disclosure, and Figma reported no evidence that it had been exploited. - Figma subsequently changed its sandbox to use a JavaScript VM written in C and compiled to WebAssembly. Figma’s experience shows that plugin systems require more than simply restricting access to browser APIs. The isolation boundary must also preserve performance and API usability, while being robust enough to withstand vulnerabilities in the underlying sandbox technology.

Read original(opens in new tab)
figma2 min readCurated summary

File loading, dragging & zooming is up to 3x faster | Figma Blog

Figma argues that performance is central to making a design tool feel like an extension of the user’s mind. A months-long effort to restructure its renderer and fix WebAssembly issues produced major gains, including up to 3× faster loading, zooming, and dragging in dense files. The improvements were measured not only by average speed, but also by interaction smoothness and frame-time consistency. ## Performance as a Core Product Requirement - Delays undermine the feeling of direct manipulation, much like a hammer lagging behind the user’s hand. - Figma continuously profiles real user documents to identify targeted optimizations. - Larger organizations increasingly use complex files with deeply nested components and many permutations, making performance more challenging. - The latest work focused on restructuring the document renderer and resolving WebAssembly bugs. ## File Loading: Up to 3× Faster - A large Microsoft Fluent Design document improved from approximately 29 seconds to under 8 seconds. - WebAssembly optimizations and other renderer changes reduced computational overhead. - WebAssembly support was enabled across: - Figma’s desktop app - Chrome - Firefox - Safari - macOS and Windows - The improvements were especially valuable for dense design-system files containing deeply nested components. ## Smoother Zooming and Dragging - Zooming and dragging are continuous interactions where responsiveness matters more than total operation duration. - Figma reduced visible “hitches” in these interactions, with improvements of up to 3×. - Dense files containing many bitmap images benefited substantially. - Figma also worked directly with customers such as N3TWORK to diagnose performance issues and test the new renderer. - Users reported immediately noticeable improvements in component publishing and file loading. ## Measuring Smoothness with Frame Time - A 500 ms operation can feel very different depending on whether it provides continuous visual feedback or freezes until completion. - Figma tracks two metrics: - **Average frame time:** Indicates overall choppiness or low frame rate. - **Maximum frame time:** Reveals occasional long pauses or “hitches.” - High maximum frame times feel like sudden interruptions, while high average frame times make motion consistently choppy. - Monitoring both metrics gives a more complete view of interaction quality than measuring total operation time or average frame rate alone. - Although a steady 60 frames per second is the ideal, tracking these metrics over time helps Figma evaluate progress toward that goal. Figma’s work demonstrates that performance optimization should focus on both raw speed and perceptual smoothness. For interactive tools, renderer architecture, WebAssembly execution, and detailed frame-time measurement are all essential to making complex documents feel responsive.

Read original(opens in new tab)