Hey — I’m phox, solo dev. I’d rather show this than pitch it.
The problem is dumb and specific: you design your UI in Figma because Figma is good at that, then you rebuild the whole thing by hand in Studio. Frame by frame, colour by colour, exporting icons one at a time into the Asset Manager. I hated that part enough to spend months deleting it.
Framewisp takes a Figma frame and gives you real Roblox instances — Frames, TextLabels, TextButtons, ScrollingFrames, with UICorner, UIStroke, UIGradient and constraints. Not one flat image pretending to be a UI.
How it works
- Select a frame in Figma → Convert
- You get a 6-character code
- Paste it into the Framewisp plugin in Studio
No API keys, no manual asset uploads, no .rbxm passing around. A code stays valid for 24 hours, so you can re-import as many times as you like.
Images from your design upload to your own Roblox account through the official Roblox OAuth flow. Nothing to paste, no credentials touch my server.
One thing worth knowing up front: Roblox reviews every newly uploaded image, so until it’s approved you’ll see a striped placeholder instead of your art. Usually minutes. That’s the moderation queue, not the plugin.
Interactivity without writing scripts
This is the part I’m most proud of. You name a layer, and it works in Play mode.
A button that opens a panel — _open:Shop on the button, _panel on the window:

A close button — just _close on the X. It hides the panel it sits inside:

And buttons get hover/press feedback automatically — no variants needed, though you can draw your own:

The plugin injects one small LocalScript that reads these tags. You write zero Lua. Delete that script and the UI stays — only the behaviour stops.
Full tag reference
Structure
| Tag | Effect |
|---|---|
_button |
frame → TextButton, image → ImageButton, with hover + press feedback |
_scroll |
→ ScrollingFrame with AutomaticCanvasSize = Y |
_canvas |
forces a CanvasGroup |
_aspect |
adds a UIAspectRatioConstraint locked to the layer’s Figma ratio |
_ignore |
layer is dropped entirely — never exported |
_keep |
pulls the layer out of a baked background so it stays its own instance |
_image |
bakes this layer to a crisp image (#Name, img:Name and _lock are aliases) |
_nodim |
this image isn’t darkened by the button hover effect |
Menus
| Tag | Effect |
|---|---|
_open:Shop |
shows the panel named Shop — springy pop-in |
_close / _exit |
hides the nearest _panel ancestor (or the whole UI if there is none) |
_toggle:Shop |
one button that opens and closes |
_panel |
marks a frame as a closable panel |
_tab:Weapons |
tab switching — shows one panel, hides the rest of the group |
_tabgroup |
put this on the tab bar’s container, so each bar is its own group |
_active |
a variant shown while its tab is selected (works inside _tab buttons) |
States — drawn in Figma, swapped at runtime
_hover and _clicked variants import hidden and appear on the matching event. They can sit anywhere inside the button, nested groups included.
Live data
| Tag | Effect |
|---|---|
{Coins} |
text bound to the local player’s leaderstats.Coins, updates by itself |
{PlayerName} |
the local player’s name |
_bind:leaderstats.Gems |
binds the whole text to one value |
Two rules worth knowing: the :Name part is required (a bare _open does nothing on purpose, so a layer called “Menu_open” isn’t hijacked), and the behaviour scripts are LocalScripts — so it all runs in Play, not in the editor.
The honest part about text
Roblox has a fixed font list. So:
- Plain text arrives as a live TextLabel in the closest matching Roblox font. Editable, scriptable, normal.
- Styled or custom-font text (gradients, textures, outlines, a font Roblox doesn’t have) is baked into a pixel-perfect image, because Roblox genuinely cannot rebuild it natively.
Nothing is baked silently — the plugin lists which layers it wants to bake and why. One switch bakes all the flagged ones; tag a single layer _image if you only want that one exact.
Auto-sync
Flip a toggle and your Figma edits land in Studio by themselves, seconds after you stop editing. Re-importing diffs into the existing tree rather than dropping a duplicate — your own scripts and hand-added children stay exactly where you put them.
Responsive by default
Everything is Scale-based (UDim2.fromScale) with an aspect lock, plus a runtime script that recalculates TextSize for the real screen, so it holds up on phones with no extra work. Buttons respond to touch — Activated fires on tap.
What it does NOT do
I’d rather you read this here than find out later.
- No auto-layout. Figma auto-layout doesn’t become UIListLayout — positions come through as absolute coordinates. Same for Figma constraints.
- No blend modes. Roblox UI has none. An Overlay/Screen layer arrives plain — set it to Normal in Figma, or bake that layer.
- No backdrop blur. Frosted glass can’t sample what’s behind it in Roblox; it bakes to a flat image of what Figma showed.
- Strokes are simplified — one solid colour, one width. No dashed, gradient, or per-side strokes.
- One corner radius per element. Mixed per-corner radii collapse to a single value.
- 30 new images per convert. Repeats are deduplicated, so it’s 30 distinct pictures; split very large screens into a couple of imports.
- It won’t design for you. Garbage in, garbage out — just faster.
Getting it
Both plugins are free to install:
- Figma plugin: https://www.figma.com/community/plugin/1657351737524801007
- Studio plugin: https://create.roblox.com/store/asset/95374365998986/Framewisp
7-day free trial, no card required. After that it’s a paid subscription; one licence covers one Roblox account. Docs and details: https://framewisp.com
Feedback
I’m one person and I read everything. If something breaks, tell me — I’ve been shipping fixes same-day. Honestly, bug reports are more useful to me right now than compliments.
Fastest way to reach me is Discord — I’m there daily and usually reply within the hour: Framewisp
What would you want it to do that it doesn’t?