FigSync LITE
The free way to bring Figma into Unity. Paste a Figma URL. Press Sync. Your frame lands in the scene as a real UGUI Canvas — with the right anchors, the right gradients, and the right rounded corners. FigSync LITE imports one frame per run; FigSync Full adds unlimited frames, incremental re-sync, the live Figma plugin and much more.
LITE vs Full — at a glance
| Feature | FigSync LITE (Free) | FigSync Full |
|---|---|---|
| Figma → Unity UGUI import | ✔ | ✔ |
| Layout, anchors, images & text | ✔ | ✔ |
| Gradients & rounded corners | ✔ | ✔ |
| Online import (Figma URL + token) | ✔ | ✔ |
| Frames per import | 1 | Unlimited |
| GameObjects per import | 100 max | Unlimited |
| Font matching, Google Fonts download & TMP atlases (LITE uses default font) | ✘ | ✔ |
| Incremental Sync & Diff (re-import & merge) | ✘ | ✔ |
| Live Figma Bridge (plugin push) | ✘ | ✔ |
| Offline ZIP import | ✘ | ✔ |
| FigJam support | ✘ | ✔ |
| Interactive components (Button / Toggle / Input / ScrollView) | ✘ | ✔ |
| Responsive auto-layout (LayoutGroups) | ✘ | ✔ |
| Shadows & blur effects | ✘ | ✔ |
| 9-slice borders | ✘ | ✔ |
| Sprite & prefab reuse (de-dup) | ✘ | ✔ |
| Code generation (binding scripts) | ✘ | ✔ |
| Localisation (Unity Localization / i2) | ✘ | ✔ |
| Import events (UnityEvents) | ✘ | ✔ |
⚡ Quick Start
Open Hub, paste URL, click Sync. First sync usually under 60 s for a typical screen.
🔒 Authentication
Personal Access Token, OAuth, or the offline Figma plugin. Pick one.
⚙ How It Works
A 27-step deterministic pipeline. Nodes → FlatList → classify → draw → finalise.
📐 Auto-Layout
Figma H/V/Grid auto-layout maps to UGUI LayoutGroup + ContentSizeFitter.
🌈 Gradients
Linear, Radial, Angular, Diamond — rendered procedurally in a single SDF shader.
☀️ FigJam Support
Stickies, connectors, shape-with-text. Plus the signature dotted canvas backdrop.
This document covers the FigSync workflow. Start with Installation and Quick Start; go deep on How It Works for the pipeline detail; reach Troubleshooting when something doesn't import the way you expect. Features marked FULL are part of FigSync Full, not the LITE edition.
Installation
Import once. The Hub window lights up from the menu.
From the Unity Asset Store
In Unity 2021.3 or later, open
Window → Package Manager and switch the dropdown to My Assets.Accept the import dialog with everything checked. The package lands at
Assets/FigSync/.Unity recompiles editor scripts. First-time compile takes ~30 s; later imports are faster.
Tools → FigSync. Everything you need — URL, sync, settings, status — is in one window.Prerequisites
FigSync uses Unity packages that ship with a default Unity 2021.3+ install:
com.unity.ugui(the Canvas / UGUI runtime).com.unity.textmeshpro≥ 3.0 (text rendering — TMP Essential Resources required).com.unity.nuget.newtonsoft-json≥ 3.2.1 (JSON serialisation).
If TMP Essential Resources isn't imported yet, the first sync will surface a one-time warning. Run Window → TextMeshPro → Import TMP Essential Resources and re-sync.
What gets installed
Assets/FigSync/
Editor/ // Pipeline, drawers, settings, hub window
Bridge/ // Local HTTP bridge for the Figma plugin
Drawers/ // One drawer per Unity UGUI component
Hub/ // Tools → FigSync window + settings store
Pipeline/ // Numbered Step01..Step26 deterministic flow
Settings/ // Per-tab settings UI (UI Toolkit)
Web/ // Figma REST + Google Fonts clients
Runtime/ // Shaders, helper components, ScriptableObjects
Components/ // FigSyncSyncHelper, dropshadow, blur, etc.
Shaders/ // SDF rounded-corner + gradient
Generated/ // Per-sync output: sprites, fonts, snapshot, report
Documentation/ // You are hereProject
Generated/ folder is yours. FigSync writes downloaded sprites, fonts, and the diff snapshot here. It's safe to re-sync anytime; the folder is rebuilt deterministically.Figma Plugin FULL
The fastest path for FigJam files, complex frames, or offline sync.
FigSync ships with a Figma plugin called FigSyncExporter. Installed in Figma, it captures the full plugin-API node tree (which is richer than REST), bundles every fill image and pre-rendered vector raster, and pushes the result to Unity over a local HTTP bridge. The Unity Hub picks up the push automatically — no copy/paste of node ids.
Why use the plugin path
Stickies, connectors, shape-with-text only appear in the plugin's node tree — FigJam files won't import via REST.
Hit Send to Unity in Figma; Unity starts the sync. No personal-access token to create and no URL to paste — you push straight from the file you're designing.
Each push saves a .figsync.zip bundle locally; you can re-sync from disk later without Figma being open.
Plugin API exposes fillGeometry path data on every vector node; REST is occasionally sparser.
Install the plugin
Menu → Plugins → Development → Import plugin from manifest...In Unity, run
Tools → FigSync → Open Figma Plugin Folder (or browse to Assets/FigSync/FigmaPlugin/), then select manifest.json. Tip: Tools → FigSync → Set Up Figma Bridge… walks you through this with live connection status.In Unity, open
Tools → FigSync → Settings → Auth. The status pill should read Bridge: listening on 127.0.0.1:8473.Open the FigSyncExporter plugin from your Plugins menu, choose scope (Current page / All pages), click Export. Unity receives the bundle and starts a sync.
127.0.0.1 and accepts only CORS requests originating from Figma's domains. No external network traffic.Quick Start
Zero to a synced Canvas in about a minute.
Menu:
Tools → FigSync. The window has tabs for Source, Settings, and Status.Paste a Personal Access Token via
Tools → FigSync → Set Figma Personal Access Token (or the token badge in the Hub).Copy a frame URL from Figma (right-click frame → Copy link). Paste into the Figma URL field in the Hub's Sync tab.
The pipeline runs its deterministic steps with per-step status. LITE imports one top-level frame per run; first sync takes ~10-90 s depending on sprite count.
A FigSync Canvas GameObject appears in the active scene. The frame renders in the Game view as a static UGUI hierarchy — layout, images and text (default font).
How It Works
A 27-step deterministic pipeline, runnable resumable cancellable.
Each sync runs as a sequence of named steps. Steps log their start and finish to the Console (look for the [FigSync] prefix); the Hub window mirrors the same status as a per-step progress strip. A failed step stops the pipeline early; the scene either keeps the prior state intact (if the failure happened pre-Draw) or surfaces the partial result with a HelpBox explaining where it broke.
Steps, in order
| Step | What it does |
|---|---|
DownloadNodes | Loads the Figma node tree from REST, the plugin bundle, or local cache. |
WrapInPage | Ensures the document root is wrapped in a CANVAS / PAGE node for uniform traversal. |
ClearCaches | Resets per-sync sidecars (UnchangedNodeIds, DeferredNodeIds, external-children rescue map). |
Flatten | Walks the tree depth-first; produces a FlatList of NodeAux entries with parent/depth indices. |
Tag | Tags each node by FigmaType + manual sidecar markers (Frame, Text, Image, Vector, Mask, Shadow, Page, etc.). |
ClassifyImages | Decides which nodes route through the PNG raster path vs the procedural draw path. |
PropagateInsideDownloadable | Marks descendants of a Downloadable parent so they aren't drawn separately. |
Hash | Computes a 128-bit content hash per node (visual properties + children) using xxHash3. |
NameFolders / NameFiles / NameStylesheetClasses | Resolves Figma names through the sanitiser into Unity-safe folder, file, and class names. |
PickRootFrames | Identifies top-level frames worth surfacing in the diff window. |
ShowDiffWindow | If a prior snapshot exists, compute the diff and prompt the user to confirm or defer roots. |
LoadPrefabMatches | Wires Figma node ids back to existing scene GameObjects with matching FigSyncSyncHelper stamps. |
DrawHierarchy | The main draw pass. One GameObject per Figma node; one drawer per tag. |
RePickRootFrames / CountTags / SpritePaths | Post-Draw planning for sprite IO. |
DownloadSprites / FlushSprites | Pulls /v1/images PNGs (or reads them from the plugin bundle); writes them to Generated/. |
GenerateSprites / SliceSprites / ColorizeSprites | Procedural sprite synthesis for nodes that don't need raster (white quads, solid fills). |
DedupeSprites | Hash-merges visually identical sprite outputs so the project doesn't double-store them. |
DownloadFonts | Resolves font families through Google Fonts (Webfonts API or fontsource fallback), bakes TMP _SDF assets. |
FrameworkFinalise | Per-backend post-pass: rounded corners, shadows, layout drivers, text binding, FigJam canvas dots. |
DiagnosticReport | Writes _figsync_report.json (telemetry) and _figsync_snapshot.json (hash snapshot for next-run diff). |
What happens at Draw time
The Draw step iterates the FlatList. Each entry is either reused, deferred, or freshly drawn:
- Reused — node's hash matches the prior snapshot AND a scene GameObject with the matching
FigSyncSyncHelper.NodeIdstill exists. Re-parented under its updated parent; no children re-created. - Deferred — user unticked the root in the diff window. Same as Reused (preserve existing scene state), but new descendants under that root are skipped entirely.
- Drawn fresh — the right drawer is resolved by tag (Image, Text, Vector, Button, Toggle, etc.), instantiates a GameObject, sets RectTransform, applies fill + text + auto-layout, stamps a
FigSyncSyncHelperwith the node id and hash.
What happens at Finalise time
After every node has a GameObject, a per-backend Finalise pass runs second-pass work that needs the full hierarchy in place:
- Bind TMP fonts to text components from the per-family
_SDF.asset. - Add
FigSyncRoundedCorneron every node with non-zero corner radius. The component encodes radius + stroke + shadow into vertex uv channels so a single SDF shader draws them all. - Attach
FigSyncDropShadowon every node with a DropShadow effect;FigSyncBackgroundBlurCompositoron every node with BackgroundBlur. - Apply auto-layout drivers (LayoutGroup + ContentSizeFitter + spacer injection for SPACE_BETWEEN).
- If the source is FigJam, inject the dotted canvas backdrop as the first sibling under each page.
Authentication
Three options. Pick one. Or two.
Option 1 — Personal Access Token
Simplest path. Generate a token at figma.com/settings (Personal access tokens section), paste it into Tools → FigSync → Settings → Auth → Personal Access Token. The token stays in EditorPrefs (base64-obfuscated, never written to assets) and is sent only over HTTPS to Figma's REST API.
Option 2 — OAuth
Click Sign in with Figma in the Auth tab. A browser tab opens Figma's consent screen; after approval Figma redirects to a one-shot local listener on 127.0.0.1 with the auth code. FigSync exchanges that for an access + refresh token pair and stores both in EditorPrefs (same obfuscation as the PAT). Refresh tokens rotate automatically; expired tokens trigger a transparent refresh on next call.
Option 3 — The Figma plugin FULL
No tokens needed. Install the plugin in Figma and push directly. The plugin reads node data using its own Figma plugin sandbox permissions; FigSync receives an HTTPS-style ZIP bundle over a loopback port. No credentials leave your machine.
| Token | OAuth | Plugin | |
|---|---|---|---|
| Setup time | 30 s | 1 min (browser hop) | 2 min (plugin install) |
| Re-auth needed | Never (until you revoke) | Refresh auto-rotates | Never |
| FigJam files | No (REST limitation) | No (REST limitation) | Yes |
| Offline sync | No | No | Yes (saved ZIPs) |
| Rate limited | Yes (Figma policy) | Yes (Figma policy) | No |
| What's stored | One token in EditorPrefs | Access + refresh tokens in EditorPrefs | Nothing |
The Hub Window
One window, every control. Tools → FigSync.
Source tab
The everyday surface. Paste a Figma URL, choose the source mode (Live URL, Offline ZIP, Bridge push), and click Sync. A status strip across the top of the window mirrors what's happening behind the scenes — current step, elapsed time, last warning. A right-side panel shows the last sync's diagnostic summary (node count, sprite count, fonts bound, missing fonts, anything that went sideways).
Settings tabs
Each tab edits one slice of the FigSyncProjectSettings ScriptableObject (lives at Assets/FigSync/FigSyncProjectSettings.asset; commit it). Changes apply on next sync.
- Auth — Personal Access Token (used by Online import).
- Main Settings — output folder, backend (UGUI), render scale, debug verbosity.
- Images & Sprites — sprite max dimension, alpha handling. FULL dedup & 9-slice.
- Text & Fonts FULL — font-family matching, Google Fonts, TMP atlas (LITE uses the default font).
- Buttons FULL — how Figma "Button" tags map to UGUI Button / Toggle / custom.
- Shadows FULL — DropShadow / InnerShadow quality, background-blur toggle.
- Localisation FULL — bind keys for Unity Localization or I2 Localization.
- Prefab Creator FULL — turn synced frames into prefabs automatically.
- Script Generator FULL — emit per-frame C# reference classes alongside the synced GameObjects.
- Import Events FULL — per-step Pre/Post import callbacks.
- Debug — verbose logging, diagnostic reports, snapshot management.
In LITE the FULL tabs appear as locked upgrade cards.
Diff & Re-sync FULL
Re-sync without losing the work you did between syncs.
After every successful sync FigSync writes a hash snapshot of the node tree to Generated/_figsync_snapshot.json. On the next sync, the pipeline diffs the incoming nodes against that snapshot before any scene mutation. The result is a per-root summary of what would change.
The diff window
When there's at least one change, the diff window opens before DrawHierarchy:
- Per-root summary chips —
+added ~modified −removedcounts at the page level. - Foldable groups — one per root frame, with per-node entries inside. Click to expand a group.
- Per-root Apply toggles — untick a root to defer it. Deferred roots aren't touched this sync; their previously-drawn GameObjects stay exactly as you left them.
- Continue / Cancel — Continue applies everything not deferred; Cancel aborts the sync with the scene unchanged.
What "preserve" means at the GameObject level
Three cases survive a re-sync identically:
Material overrides, animation triggers, references wired in the inspector — persisted on the unchanged GameObjects.
Anything you've added — controllers, behaviours, audio sources — stays attached to the same GameObject across syncs.
Non-FigSync children dropped under a synced GameObject are rescued and re-parented to the freshly-drawn replacement if the parent's hash changed.
First sync, no snapshot
The diff window is skipped on first sync — nothing to compare against. The pipeline writes a snapshot at the end. Every sync from the second onwards benefits from the diff path.
Auto-Layout FULL
Figma's auto-layout maps to UGUI LayoutGroup + ContentSizeFitter end-to-end.
A Figma frame with auto-layout becomes a Unity GameObject with the matching HorizontalLayoutGroup, VerticalLayoutGroup, or GridLayoutGroup, plus a ContentSizeFitter when the sizing mode is "Hug contents". Children flow in the same direction, with the same padding, gap, and primary/counter alignment.
What's wired
- Direction (Horizontal / Vertical / Grid) — chooses the LayoutGroup component.
- Padding & gap (the four sides + itemSpacing) — LayoutGroup
padding+spacing. - Primary alignment (Start / Center / End / Space Between) — LayoutGroup
childAlignment; SPACE_BETWEEN uses injected invisibleFigSyncLayoutSpacerchildren for the slack. - Counter alignment — aligns the off-axis children to start/centre/end of the cross axis.
- Sizing (Fixed / Hug contents / Fill container) — combination of
ContentSizeFitter+LayoutElement. - ABSOLUTE children (a child of an auto-layout frame with "absolute position" toggled in Figma) get a
LayoutElement.ignoreLayoutso they keep their baked anchored position instead of flowing.
What's not wired
See the dedicated Known Limitations for the specifics:
- Auto-layout Wrap mode falls through to free-position layout.
- Grid with non-uniform tracks (per-cell column / row sizes) flattens to uniform cells.
- Constraints on flow children are intentionally ignored; the LayoutGroup owns placement.
Constraints (Anchors)
Figma per-axis constraints drive UGUI anchors so layouts respond to Canvas resize.
Every Figma child carries a per-axis constraint (Min / Max / Center / Stretch / Scale). FigSync maps them onto the child's anchorMin / anchorMax + offsetMin / offsetMax so the child reacts the right way when its parent rect changes size.
| Figma constraint | Horizontal effect | Vertical effect | Unity anchors |
|---|---|---|---|
Min | Pinned to left | Pinned to top | (0,1) — (0,1) |
Max | Pinned to right | Pinned to bottom | (1,0) — (1,0) |
Center | Centered horizontally | Centered vertically | (0.5,0.5) — (0.5,0.5) |
Stretch | Resizes with parent (L/R) | Resizes with parent (T/B) | (0,*) — (1,*) |
Scale | Scales proportionally | Scales proportionally | (0,0) — (1,1) |
Re-sizing the root Canvas at runtime should re-flow the design exactly the way it would in Figma's preview.
Gradients
Four gradient kinds, rendered procedurally in one SDF shader.
FigSync renders Figma gradients on the GPU instead of baking them to a texture. Four kinds are supported: GRADIENT_LINEAR, GRADIENT_RADIAL, GRADIENT_ANGULAR, and GRADIENT_DIAMOND — the full set Figma can author. Multi-stop ramps up to 4 stops render natively; 5+ stop ramps collapse to first + last + two middle stops (see Known Limitations).
How gradients render
The same FigSync/RoundedCorner SDF shader that handles rounded corners also handles gradients. The two are composed at the fragment level: gradient sampled first, alpha multiplied by the corner SDF, output to screen. One Image + one Material per node, no extra draw call.
What's preserved
- Gradient handles — the start/end points (Linear), centre + radius (Radial), centre + rotation (Angular), centre + width/height (Diamond) are passed as material uniforms.
- Per-stop colours & alpha — encoded in the four-stop ramp uniform.
- Gradient on top of a stroke — works the same way; stroke renders at the SDF edge band.
- Gradient on a rotated rect — rotation is baked into the rect's local coords; the gradient stays aligned to Figma's intent.
Rounded Corners
SDF rounded corners with per-corner radii, stroke band, drop shadow, inner shadow.
Nodes with a non-zero corner radius receive a FigSyncRoundedCorner runtime component (built on Unity's BaseMeshEffect). The component encodes the corner radii, stroke width, drop-shadow offset, and inner-shadow offset into the mesh's UV channels — one shader draws every effect against the SDF distance field, no extra GameObjects or quads.
What's encoded
- Per-corner radii — Figma's
rectangleCornerRadiiare read directly; uniformcornerRadiussets all four equal. - Stroke band — stroke weight + alignment (Inside / Center / Outside) drives the SDF outline thickness.
- Drop shadow — up to four stacked drop shadows (Figma allows arbitrary; we render the first four). Offset, blur, spread, colour per layer.
- Inner shadow — up to four stacked inner shadows.
- Gradient fill — composed at the same fragment stage (see Gradients).
Why an SDF shader instead of PNG raster
SDF stays crisp at any scale — rounded buttons look right at 0.5x and 4x zoom alike. PNG raster would force a fixed-resolution bake that aliases under scale. The SDF path also unifies stroke + corner + shadow into a single draw call, so a screen with 50 rounded panels is 50 draw calls, not 200.
Shadows & BackgroundBlur FULL
Drop shadow, inner shadow, layer blur, and Figma's signature glass effect.
Drop & inner shadow
Up to four stacked drop shadows and four stacked inner shadows per node, all rendered against the same SDF. No extra GameObjects, no overlay quads — just additional uniforms on the shared material. Stacking is preserved: Figma's first shadow is the topmost layer.
Layer blur
A Figma LayerBlur effect on a leaf node renders procedurally by widening the SDF edge. On a node with children, the procedural path can't blur the descendants — FigSync routes the whole subtree to the PNG raster path where Figma bakes the blur server-side, then displays the result as a Unity sprite.
Background blur (the glass effect)
A Figma BackgroundBlur blurs whatever sits behind the consumer node — the iOS-style frosted-glass look. FigSync ships a procedural backdrop:
- A
FigSyncBackgroundBlurCompositorsnapshots the Canvas's Render Camera into a RenderTexture once per frame. - Each consumer node samples that RT through a Gaussian blur kernel matching the radius from Figma.
- The consumer's own fill (a semi-transparent tint) composites over the blurred sample.
Text & Fonts
TMP text rendering with mixed inline styles. Font matching & download are FULL.
Text rendering
Every Figma TEXT node becomes a Unity TextMeshProUGUI. Size, alignment, line height, letter spacing, and colour map directly. Multi-line text wrap respects the bbox width; single-line bbox-height nodes get NoWrap so labels overflow horizontally instead of stacking.
Automatic font download FULL
The DownloadFonts step resolves every font family used in the file through:
- Your Google Fonts API key, if you've set one in Settings → Auth (returns TTFs from gstatic.com directly).
- The public Google Fonts CSS endpoint as fallback.
- The Fontsource CDN for variable fonts Google ships only as WOFF2.
- The Adobe Edge font index for non-Google families.
Each resolved TTF is imported as a Unity Font and baked into a TMP _SDF.asset at Generated/Fonts/<Family>-<Weight>.asset. On subsequent syncs the baked asset is reused — no re-download.
Fonts that didn't resolve FULL
For proprietary families (Whyte, Figma Hand, custom) or Google variable fonts that don't ship a TTF download path (Inter, Newsreader), the import surfaces a one-shot dialog at the end of the sync listing what to drop in. Save the TTFs to Assets/FigSync/Generated/Fonts/ using the convention <Family>-<weight>.ttf (e.g. Inter-400.ttf); the next sync auto-binds them to every matching text component.
Mixed inline styles
A single Figma text node can mix weights, italics, underlines, and strikethroughs per character. FigSync reads the per-segment styling and emits TMP rich-text tags (<b>, <i>, <u>, <s>) for the segments that diverge from the node-level base style. Cross-family inline segments keep their weight + italic but render in the base family asset (see Known Limitations for the TMP atlas reason).
Sprites & Atlases
Auto-classified, auto-deduped, auto-sliced.
Some nodes can't be drawn procedurally — complex vector icons, photo fills, baked illustrations. For those, FigSync pulls a PNG from Figma's /v1/images endpoint (or reads the pre-rendered PNG from the plugin bundle), saves it under Generated/<FolderName>/<NodeName>.png, and binds it as an Image.sprite.
How classification works
The ClassifyImages step decides per-node whether to route to the raster path:
- Photos & complex vectors — raster.
- Pure solid / gradient rectangles — procedural.
- Vector paths with simple fills — meshed via
FigSyncVectorGraphic(triangulated on the CPU, drawn as a UGUI graphic). - Nodes inside a baked parent — suppressed; the parent's PNG already contains them.
Dedup FULL
The DedupeSprites step hashes every freshly-written PNG; duplicates (same pixel content) collapse to a single asset on disk with the other nodes pointing at it via reference. Cuts asset count and binary size sharply on screens with repeated iconography.
Slicing (9-slice) FULL
The SliceSprites step looks for the manual sidecar convention — a node named BG/9slice or with a tag suffix — and writes 9-slice border data into the sprite asset's border field so UGUI Image rendering with type Sliced reuses the same texture across multiple sizes.
FigJam Support
Stickies, connectors, shape-with-text, and the signature dotted canvas.
FigJam files are first-class. The pipeline auto-detects FigJam at the editor level (the editorType field in meta.json, with a node-type heuristic fallback for older exports) and switches on FigJam-specific rendering paths.
What's rendered natively
- Sticky notes — baked from Figma's
/v1/imagesPNG so the body + text + colour render as one sprite. - Connectors (the no/yes/etc. arrows) — same path, baked PNG.
- Shape-with-text (Buy ice cream / Has ice cream? / etc.) — baked PNG with the inset text included.
- Sections — treated as plain frames.
The dotted canvas backdrop
FigJam's signature dotted-grid canvas isn't a Figma node — it's painted by FigJam itself behind whatever's on the page. Frames that look "white on left, dotted on right" actually have a transparent right half with the canvas dots showing through. FigSync detects FigJam files and injects a _FigJamCanvasDots Image as the lowest sibling under each page. The tile is 24×24 px, opaque, with a soft grey dot — matches FigJam's rendered look in side-by-side comparisons.
Inline icons over text
FigJam tutorial frames often place an icon (.key / ctrl / esc) over a run of whitespace inside a text node ("Press and drag"). Figma renders the space narrow enough to fit the icon; TMP renders it wider and forces an early wrap. FigSync rewrites runs of 3+ consecutive spaces as <space=Npx> TMP tags so the line wraps where Figma intends it to.
Settings
One ScriptableObject. Safe to commit.
Settings live as a ScriptableObject at Assets/FigSync/FigSyncProjectSettings.asset. Edit through the Hub window's tab views, or select the asset and use the Inspector.
| Field | Default | Description |
|---|---|---|
Output Folder | Assets/FigSync/Generated | Where sprites, fonts, snapshots, and reports land. Must be inside Assets/. |
Backend | UGUI | Target framework. UGUI is the shipping backend. |
Force Refresh | false | Skips the 24h REST cache for the next sync. Use when Figma was edited very recently and you don't want a stale node tree. |
Bridge Port | 8473 | Local HTTP port the Figma plugin pushes to. Change only on collision; tell the plugin to match. |
Render Mode | Screen Space - Camera | Default Canvas render mode for new imports. Switch to World Space for VR/AR or 3D-anchored UI. |
Debug Verbosity | Info | Console log level — Silent / Errors / Warnings / Info / Verbose. Verbose prints per-node decisions. |
Tokens (Personal Access Token, OAuth refresh, Google Fonts API key) are stored separately in EditorPrefs with base64-XOR obfuscation. They never write to assets or version control.
Known Limitations
What FigSync doesn't fully render, why, and how to work around it.
Gradients — capped at 4 stops
The SDF shader carries a 4-stop ramp uniform. Figma ramps with 5+ stops are flattened to first + last + two middle stops by FigmaJsonDeserialiser.AppendStop. Rare in practice.
Gradients — angular 2-stop seam
frac() produces a hard edge at the axis direction for 2-stop angular ramps. Designers can hide this by adding a 3rd wrap-around stop whose colour matches the first stop at position 1.0.
Auto-layout — Wrap mode not implemented
Figma's HORIZONTAL, VERTICAL, and GRID auto-layout modes are wired end-to-end. WRAP (a horizontal layout that wraps to a new row when it overflows) falls through to free-position layout. Designs using wrap render with baked positions and won't reflow.
Auto-layout — Grid is uniform cells only
UGUI's GridLayoutGroup requires one cellSize for every cell. Figma's auto-grid model supports variable column widths and row heights ("track sizing"); FigSync flattens that to equal cells derived from frame size minus padding/gaps. Visually correct when the designer used "fill" track sizing across the board; non-uniform tracks lose their per-cell sizes.
Auto-layout — SPACE_BETWEEN uses injected spacers
UGUI's LayoutGroup has no native SPACE_BETWEEN, so FigSync inserts invisible LayoutElement GameObjects (marked FigSyncLayoutSpacer) between every adjacent pair of children. They absorb the leftover slack equally — visually the same as Figma's SPACE_BETWEEN. Side effect: N-1 extra GameObjects per SPACE_BETWEEN frame.
Auto-layout — constraints on flow children are ignored
When a child sits inside an auto-layout FRAME and isn't marked ABSOLUTE-positioned, its Figma constraints (Stretch / Center / Pin) don't drive its anchors — the parent's LayoutGroup owns placement. The child still resizes correctly when the LayoutGroup re-flows; it just can't independently stretch to parent edges the way a free-position child can. Designers who need that behaviour should toggle the child to "absolute position" in Figma.
Gradients — runtime Image.color.a tween ignored
Gradient sampling uses per-stop alpha as the output alpha, so per-stop fades work, but a runtime Image.color.a tween on the Graphic itself doesn't dim the gradient. Designers who want a runtime fade should add a transparent stop to the ramp (or animate a CanvasGroup parent).
Effects — LayerBlur on a frame with children falls back to PNG
LayerBlur on a leaf node renders procedurally via SDF-edge widening. On a node with children, the procedural path can't blur the children, so the classifier routes the whole subtree to the Downloadable PNG path where Figma bakes the blur server-side. Slightly heavier on import, indistinguishable at runtime.
Mixed inline text — segment can't override base style downward
TMP's rich-text grammar has <b> / <i> / <u> / <s> to turn flags on, but no inline "force off" counterpart. If the node-level base style is bold and a segment is regular, the regular segment still renders bold. Pick the lightest weight as the base style and let segments add emphasis.
Mixed inline text — cross-family segments stay on the base family
TMP's <font="X"> tag only resolves under Resources/Fonts & Materials/. FigSync writes baked atlases under the per-import output folder. The segment's weight + italic carry through (synth-bold / synth-italic from the base font); only the family doesn't switch. DownloadFonts still pulls the segment-specific TTF + .asset, so a future TMP_StyleSheet integration could close this gap.
BackgroundBlur — Scene view shows smeared sample
The compositor's RenderTexture is filled by the Canvas's Render Camera. In Game view the rendering camera and the source camera match, so UVs line up. In Scene view the editor camera is rendering the consumer but the RT was filled by the source camera — UVs land on unrelated pixels and "smear" as the editor camera moves. Verify the look in Game view, not Scene view.
BackgroundBlur — Overlay canvas not supported
Screen Space - Overlay bypasses Unity's camera-render pipeline, so there's no Camera.Render hook to snapshot canvas content. Switch the canvas to Screen Space - Camera and assign a Render Camera. The compositor picks up the change on its next tick.
Strokes — image-fill strokes don't render
The procedural path renders solid + gradient strokes. Image-fill strokes are not represented in the SDF shader and silently disappear. Rare; designers typically use solid colours for strokes.
Effects — inner shadow caps at 4 layers
Figma allows arbitrary stacked inner shadows; FigSync renders the first 4. Real designs with 5+ stacked inner shadows are essentially nonexistent.
Render pipelines — HDRP untested
Shaders are written in Built-in HLSL and ship URP-tested. HDRP variants haven't been validated end-to-end. The BackgroundBlur compositor specifically needs URP's render-graph to accept the depth-buffered RT.
This is the single source of truth for FigSync's known trade-offs. If you hit something not listed here, mail ragendom@gmail.com with a minimal repro.
Troubleshooting
Common failure modes, what they mean, how to recover.
Sync errors
403 Forbidden from FigmaAction requiredYour Personal Access Token doesn't have access to the file, or the token was revoked.
What to do: In Figma, confirm you have at least view permission on the file. Generate a fresh token in figma.com/settings and paste it into Settings → Auth.
429 Rate LimitedAuto-retryToo many Figma API requests in a short window.
What to do: The pipeline waits the cooldown Figma returns in the Retry-After header and resumes automatically. If it keeps tripping, wait for the cooldown to clear, or use the Figma plugin path, which exports directly from the file you're working on (no token or URL needed).
Bridge: port already in useAction requiredAnother process is holding port 8473 (or your configured Bridge Port).
What to do: Either close the offending process, or change Bridge Port in Settings → Auth and re-import the plugin manifest in Figma so they agree.
TMP Essential Resources missingAction requiredThe Text rendering relies on TMP's default fallback assets, which aren't in the project yet.
What to do: Run Window → TextMeshPro → Import TMP Essential Resources, accept the import dialog, re-sync.
Common issues
The whole canvas is white / nothing renders
The most common cause is that the Canvas's Render Camera was deleted between syncs. Check FigSync Canvas in the scene — its Canvas component should have Render Mode = Screen Space - Camera with a non-null Render Camera. Re-assign and the design appears.
Fonts are wrong / Inter when I asked for Roboto (FULL edition)
Most often: the font wasn't on Google Fonts, fontsource, or any of the public CDNs. The diagnostic dialog at the end of the sync lists exactly which families didn't resolve. Drop the .ttf files into Assets/FigSync/Generated/Fonts/ as <Family>-<weight>.ttf and re-sync.
An icon appears blurry / pixelated
The node was routed to the PNG raster path and rendered at a smaller-than-display resolution. Try resizing the source frame in Figma (Figma's /v1/images renders at the frame's absoluteRenderBounds), or convert the node to a Vector if Figma can express it that way — vector nodes mesh procedurally and stay crisp at any scale.
A shadow / corner radius is missing on one node
The node was probably classified Downloadable (raster path), in which case Figma's PNG already contains the shadow/corner baked in. If the PNG is missing the effect, check the effect's Visible toggle in Figma — FigSync respects it. For procedural-path nodes (solid colour frames), inspect the GameObject's FigSyncRoundedCorner and confirm the radii are non-zero.
Re-sync overwrote my edits
FigSync preserves edits on nodes whose hash didn't change. If a node's content changed in Figma, its hash changes, and the GameObject is rebuilt — that's the intended behaviour. To prevent that for a specific frame, untick it in the Diff Window before clicking Continue; the frame stays as-is until you re-include it.
Sync hangs at DownloadFonts (FULL edition)
Google Fonts or fontsource is unreachable, or your network is filtering them. Sync can complete without fonts — the import surfaces a Missing fonts dialog and you can drop TTFs in manually. If you're behind a corporate proxy, set the Google Fonts API key in Settings → Auth to use the Webfonts Developer API instead of the public CSS endpoint.
Enable verbose logging when in doubt
Set Debug Verbosity = Verbose in Settings → Debug and re-sync. Every step logs its inputs, decisions, and counts to the Console with the [FigSync] prefix. The diagnostic report at Generated/_figsync_report.json also captures the full per-node decision trace.
FAQ
Quick answers to the questions that come up most.
Does FigSync work with FigJam?
Yes, through the Figma plugin path. FigJam-only node types (Sticky, ShapeWithText, Connector) aren't exposed by Figma's public REST API, so the plugin is required for FigJam files. Plain Figma Design files work over both paths.
Does it work offline?
Yes, after the first sync. Each plugin push saves a .figsync.zip bundle under Library/FigSync/Bridge/; you can re-sync from that bundle without Figma open. The REST path requires internet to fetch the latest node tree (a 24-hour cache softens that).
Can I use the synced UI in builds (mobile / WebGL / console)?
Yes. The output is a plain Unity UGUI Canvas — sprites are .png assets, fonts are TMP _SDF.asset, layout is LayoutGroup + ContentSizeFitter. Nothing in the runtime is editor-only. FigSync's own runtime components (rounded corner, drop shadow, blur compositor) are plain MonoBehaviours that work in any build target.
Does it support HDRP?
The UGUI canvas itself works on any pipeline. The shipping shaders target Built-in / URP and haven't been end-to-end-tested on HDRP. BackgroundBlur specifically needs URP's render-graph; on Built-in it works without the depth buffer. We'd love HDRP reports if you hit issues.
What about other UI frameworks (UI Toolkit etc.)?
UGUI is the shipping backend in v1.0. Other UI frameworks (UI Toolkit and similar) aren't supported today.
What happens to my scene if a sync fails halfway?
Steps before Draw are read-only — if they fail, the scene is untouched. Once Draw starts, the prior snapshot is loaded; if a step fails mid-Draw, the scene shows whatever was drawn up to that point, with a HelpBox naming the failing step. The hash snapshot is only written on full success, so the next sync diffs against the last known-good state — mid-failure runs don't poison the diff baseline.
Can two designers work on the same file?
Yes — both sync the same URL. Each sync writes deterministically (same node id → same GameObject name → same file path), so a teammate pulling your branch and re-syncing produces the same scene. Commit the Generated/ folder if you want the sprites + fonts to ship with the repo; or treat it as a build artifact and re-sync on first checkout.
How big is the runtime overhead?
The synced Canvas is a plain UGUI Canvas. FigSyncRoundedCorner is a BaseMeshEffect that adds <0.1 ms per canvas rebuild for typical screens. FigSyncBackgroundBlurCompositor samples one RenderTexture per consumer per frame — cheap on desktop, noticeable on mobile if you have 10+ blurred panels visible at once. Drop shadows are mesh-vertex effects, not extra draw calls.
How do I roll back a bad sync?
Ctrl-Z. Every sync wraps its scene mutations in a single Undo group, so one undo reverts the entire sync. The Generated/ folder is also versionable — if you commit it, git revert rolls back the sprite/font side too.
My team has multiple developers. Do we each need a license?
Yes. Per Unity Asset Store policy, each seat that uses the asset needs its own license.
Support
Need help? Reach out.
Get in Touch
Bug reports, feature requests, licensing questions — reach out and we'll get back to you.
✉ ragendom@gmail.comWe typically respond within 24–48 hours.
- Check the Troubleshooting section
- Skim Known Limitations
- Set Debug Verbosity = Verbose and re-sync to capture context
- Open
Generated/_figsync_report.json— the failure is usually visible there
- Unity version (e.g.
2022.3.30f1) - FigSync version (v1.0.0)
- Render pipeline (Built-in / URP / HDRP)
- Auth path (Token / OAuth / Plugin)
- Steps to reproduce + Console output
Ready for the full workflow?
You're on FigSync LITE (free). FigSync Full unlocks unlimited frames, incremental Sync & Diff, the live Figma Bridge & offline ZIP, font matching & Google Fonts, interactive components, auto-layout, shadows & blur, 9-slice, sprite/prefab reuse, code generation and localisation.
★ Upgrade to FigSync FullEnjoying FigSync LITE?
A short Asset Store review helps other developers discover FigSync and keeps the updates flowing. It only takes 30 seconds.
⭐ Leave a Review on the Asset StoreFigSync LITE v1.0.0 · Built for Unity 2021.3+ · Upgrade to Full ★
Support: ragendom@gmail.com · Asset Store: u3d.as/1oGH