figma-kiwi-protocol
Read and write Figma files over their binary Kiwi wire protocol. Extract the full scenegraph, SVG vectors, and CSS from WebSocket frames — then push mutations back to live files from a script, no UI automation. No REST API rate limits, no paid plan required.
Ships as a lib, CLI, MCP server, and Claude Code plugin.
What this does
Figma uses Kiwi (a binary serialization format by Evan Wallace) over WebSocket for real-time sync between the editor and the server. When you open a Figma file in the browser, the entire scenegraph — every node, every vector path, every style — is streamed as Kiwi-encoded binary frames.
This library speaks that protocol in both directions.
Read — intercept frames via Chrome DevTools Protocol, decode the binary, and get structured access to everything Figma knows about your design:
- Full scenegraph as JSON (5000+ nodes for a typical file)
- SVG path extraction from Figma's proprietary
commandsBlob and vectorNetworkBlob binary formats
- CSS properties from both the decoded scenegraph and the REST API
- Component variants, prototype interactions, and state machines
- Image hash mapping to Figma's CDN URLs
Write — open a standalone Node WebSocket to Figma's multiplayer endpoint (no Chrome at runtime) and apply mutations that propagate live to every connected editor:
- Rename layers, toggle auto-layout, change padding, spacing, hug/fixed sizing
- Deep-clone a subtree into a fresh independent copy (60+ nodes in one shot)
- Author from scratch with a builder that pre-handles the wire-format gotchas
- Batch edits from a JSON file, with dry-run support
Why
The Figma REST API has aggressive rate limits and is read-only. The official MCP server requires a paid Dev Mode subscription. Community MCP servers hit the same REST API limits. Plugins run in the browser sandbox and can't be driven from a script or CI.
This tool reads and writes the same data Figma's own editor reads and writes — directly off the WebSocket. No rate limits. No paid plan. No browser at runtime once cookies are captured. Just the raw protocol.
Architecture
lib/ Pure functions — buffer in, data out. No I/O, no side effects.
├── kiwi.mjs fig-wire frame detection and schema extraction
├── scenegraph.mjs decode, merge, and query scenegraph data
├── svg.mjs commandsBlob / vectorNetworkBlob → SVG paths
├── css.mjs node properties → CSS (Kiwi + REST API formats)
├── session.mjs FigmaSession — persistent multiplayer WebSocket + mutate()
├── clone.mjs cloneSubtree — deep copy a node subtree into fresh guids
├── builder.mjs FigmaBuilder — author nodes from scratch with gotchas handled
└── index.mjs re-exports everything
bin/ CLI tools — CDP capture, file I/O, orchestration.
├── cli.mjs entry point
├── capture.mjs single page WebSocket capture
├── capture-all-pages.mjs multi-page capture (auto-discovers pages)
├── decode.mjs binary → JSON scenegraph
├── decode-frames.mjs decode every captured frame for inspection
├── extract-svgs.mjs vector nodes → individual SVG files
├── to-html.mjs scenegraph node → HTML / Tailwind
├── recon-handshake.mjs one-shot CDP: steal cookies + multiplayer URL
├── standalone-client.mjs low-level probe: full handshake + one mutation
├── figma-write.mjs high-level write CLI (rename, auto-layout, …)
├── figma-clone.mjs deep-clone a subtree into a new independent copy
└── build-footer-fresh.mjs example: author a component from scratch
The read path is CDP-attached — the user's Chrome bears the auth, we just listen. The write path is standalone: recon-handshake steals cookies and the multiplayer URL once, then FigmaSession opens its own WebSocket from Node. No Chrome at runtime, no UI automation, runs from CI.
The lib has zero runtime dependencies on the read path and returns plain objects. FigmaSession uses ws and Node 22's built-in node:zlib zstd — no native modules. Kiwi encoding/decoding uses the schema Figma itself sends us in the first WebSocket frame.
Quick start — read
npm install figma-kiwi-protocol
# 1. Open your Figma file in Chrome with remote debugging:
# chrome --remote-debugging-port=9222
# 2. Set environment variables:
export CDP_WS_URL="ws://localhost:9222/devtools/browser/<id>"
export FIGMA_TOKEN="figd_..." # for page discovery
export FIGMA_FILE_KEY="abc123def" # from your Figma URL
# 3. Capture all pages:
npx figma-kiwi-protocol capture-all-pages
# 4. Decode into JSON:
npx figma-kiwi-protocol decode
# 5. Extract SVGs:
npx figma-kiwi-protocol extract-svgs
Quick start — write