# Envy documentation --- --- > Envy is a game UI editor. Design menus, HUDs and inventories on one canvas, then run the same file natively in Godot 4 and in the browser, with a contract your programmers or coding agents can wire up. --- --- # Quick start > Make your first game UI screen in Envy (alpha), preview it, and run it in Godot 4 or a web page. Takes about ten minutes. Source: https://envyui.com/docs/quick-start Envy is a game UI editor. You draw screens on a canvas, and the same file runs natively in Godot 4 and in the browser. This page takes you from an empty project to a menu running in your game. ## 1. Open the editor Envy is in alpha: [join the waitlist](/waitlist) for access. The editor runs in a desktop browser with no install. Your work is saved in the browser as you go, and **Ctrl S** saves it to a `.envy.json` file on disk. Choose **New project**. You can start blank, from the [starter kit](/kit) (15 screens on one token set), or from a showcase project like [The Last Hunt](/showcase). ## 2. Draw a screen - Press **F** for a frame, **T** for text, **B** for a button. **V** goes back to select. - Select a few layers and press **Shift A** to put them in an auto-layout row or column. In Godot they become box containers. - Pin layers to edges with the anchor widget in the inspector, so the screen holds at other aspect ratios. See [Layout](/docs/layout). - Select a layer and press **Ctrl Alt K** to turn it into a component. Copies stay in step with it. See [Components](/docs/components). ## 3. Make it do something - On a button, add a click action: **Emit event** `start_game`, **Go to screen**, **Push** an overlay, **Set** or **Toggle** data, or **Play** an animation. - Bind a text or progress layer to a data path like `player.hp`, or write `HP {player.hp}` in a text layer. - Give the document sample data so the canvas looks alive. See [Data and events](/docs/data). Press **Ctrl Enter** to preview. Buttons, transitions and animations run for real. ## 4. Run it in your game Press **Ctrl E** to export. **Godot 4.3+**: export the **Godot package**, copy `addons/envy_ui` into your project, enable the plugin and add an `EnvyUI` node: ```gdscript var ui := EnvyUI.new() ui.document_path = "res://ui/my_ui.envy.json" add_child(ui) ui.ui_event.connect(func(event, payload): if event == "start_game": start_game()) ui.set_data("player.hp", 72) ``` **Web**: load the runtime and mount the document into any element: ```html
``` ## 5. Hand it off Export **AI handoff** to get a zip a programmer or a coding agent can work from: AGENTS.md, the event and data contract, screenshots and typed code for Godot and TypeScript. See [Handoff](/docs/handoff). ## Next - [The editor](/docs/editor): canvas, panels and shortcuts. - [Godot runtime](/docs/godot) and [web runtime](/docs/web). - [The document format](/docs/format), if you want to read or write `.envy.json` by hand. --- # The editor > How Envy's game UI editor is laid out, the tools on the canvas, previewing, saving and the keyboard shortcuts you'll use every day. Source: https://envyui.com/docs/editor The editor is a web app. The canvas in the middle is the web runtime in design mode, so what you see while you edit is what ships. ## Layout - **Layers** on the left: every screen and component board, and the layer tree of the one you're in, with a search box. - **Canvas** in the middle: screens and component boards side by side. Pan with Space or the hand tool, zoom with Ctrl + scroll. - **Inspector** on the right: layout, style, text, interaction, bindings, focus and notes for the selection. - **Timeline** at the bottom (**Alt T**): keyframe animation for the current screen. Hide panels to get room: **Ctrl \\** hides both, **Ctrl Alt \\** the left one, **Ctrl Shift \\** the right one. Drag a panel's edge to resize it. ## Selecting Click selects the top-level layer under the pointer, double-click goes one level deeper, and Ctrl-click picks the deepest layer. Drag on empty canvas to marquee-select. Arrow keys nudge by 1 px, Shift with an arrow by 10. ## Tools | Key | Tool | |---|---| | V | Select | | H | Hand (pan) | | F | Frame | | R | Rectangle | | O | Ellipse | | L | Line | | N | Pen (vector paths) | | T | Text | | B | Button | | I | Image | | P | Progress bar | ## Everyday shortcuts | Keys | Action | |---|---| | Ctrl S / Ctrl O | Save to file / open a file | | Ctrl E | Export | | Ctrl Enter | Preview | | Ctrl Z / Ctrl Shift Z | Undo / redo | | Ctrl D | Duplicate | | Ctrl G / Ctrl Shift G | Wrap in a frame / unwrap | | Shift A | Auto layout on or off (wraps a multi-selection in a row) | | Ctrl Alt K | Create a component from the selection | | Ctrl Alt B | Detach an instance | | Ctrl Alt C / Ctrl Alt V | Copy / paste properties | | Ctrl ] / Ctrl [ | Bring forward / send backward (Shift for front and back) | | Ctrl Shift H / Ctrl Shift L | Hide / lock | | Ctrl A | Select all layers next to the selection | | Shift R | Rulers | | Ctrl ; / Ctrl ' | Guides / layout grids | | Shift 1 / Shift 2 | Zoom to fit / zoom to selection | | Ctrl 0 | Zoom to 100% | | Alt T | Timeline | | ? | All shortcuts | Hold **Alt** over a layer to measure the distance to the selection. ## Saving The editor autosaves every document in your browser and shows whether the last save worked. To keep a file in your game's repository, link the document to a `.envy.json` file: after that, **Ctrl S** writes straight to it, and Godot picks up the change while the game runs (see [Godot runtime](/docs/godot)). ## Checks **Document checks** list problems before you export: missing tokens, screens, assets or focus targets, data paths without a sample value, untranslated keys and more. The same checks run in the CLI as `envy validate`. > The site shows the editor's next design (v2), which is in development. Today's editor has the same canvas, runtime and features in a simpler layout. --- # Layout > Anchors, offsets and auto layout in Envy work like Godot's Control anchors and box containers, so one layout holds at 16:9, 21:9, 16:10 and 4:3. Source: https://envyui.com/docs/layout Envy's layout model is Godot's. Every layer has **anchors** and **offsets** like a Godot `Control`, and frames can lay out their children automatically like `HBoxContainer` and `VBoxContainer`. Nothing is translated on export: the Godot runtime copies the numbers 1:1. ## Anchors and offsets Anchors are fractions of the parent rectangle, from 0 to 1, for the left, top, right and bottom edges. Offsets are pixels added to the anchored edges. | Anchors (l, t, r, b) | Behaves like | |---|---| | 0, 0, 0, 0 | Pinned to the top-left corner (the default) | | 1, 0.5, 1, 0.5 | Pinned to the right edge, centered vertically | | 0, 1, 1, 1 | Stretches along the bottom edge | | 0, 0, 1, 1 | Fills the parent | In The Last Hunt's main menu, the menu is anchored right and middle, the last-save card to the bottom right, and the header stretches across the top. Change the screen to 21:9 or 4:3 and each group stays where it belongs, with no second layout. You can see it on the [home page](/#design). In the inspector, the anchor widget sets common presets in one click, and the raw numbers are there when you need them. ## Auto layout A frame or button can arrange its children in a **row** or a **column** (Shift A toggles it): - `gap` between children, `padding` inside the frame. - `align` on the cross axis (start, center, end) and `justify` on the main axis (start, center, end, space-between). - `wrap` moves children onto new lines, with `lineGap` between lines. Children inside auto layout size themselves with **fixed**, **fill** or **hug** on each axis. | Envy | Godot | Web | |---|---|---| | Row / column | `PanelContainer` › `HBoxContainer` / `VBoxContainer` | Flexbox | | Wrap | `HFlowContainer` / `VFlowContainer` | `flex-wrap` | | Padding | Content margins | Padding | | Fill | `SIZE_EXPAND_FILL` | `flex: 1` | | Space-between | Expanding spacers | `justify-content: space-between` | ## Scaling Both runtimes scale the screen's design resolution to fit the window and stretch the root to the window's aspect ratio, like Godot's `canvas_items` stretch mode with `expand`. Anchors decide how each layer follows. ## Rotation `rotation` turns a layer around its center in degrees. It doesn't affect layout. ## Design aids Rulers (Shift R), guides dragged off the rulers, and layout grids (columns, rows or a square grid) help you line things up. Layers snap to guides and grid edges. Runtimes ignore all of them. --- # Style, tokens and fonts > Fills, gradients, image fills, borders, shadows, blur, text styles, color tokens and embedded fonts in Envy, and how each maps to Godot and CSS. Source: https://envyui.com/docs/style ## Paint Frames, buttons and shapes have a **fill** (a color or a `$token`), or a linear or radial **gradient** instead. An **image fill** paints an image over that, clipped to the corners, as cover, contain, stretch or tile. On top of the main fill, frames and buttons can stack **extra fills** and **extra strokes**, each with its own opacity, drawn under the children. | Property | Notes | |---|---| | `radius` | Per corner | | `border` | Drawn inside the layer, never affects layout | | `shadow` | x, y, blur, color | | `innerShadow` | x, y, blur, spread, color; above the fill, below the children | | `backdropBlur` | Blurs what is behind the layer (frosted glass) | | `layerBlur` | Blurs the layer itself and its children | | `opacity`, `clip` | Group opacity and clipping | In Godot, paint becomes a `StyleBoxFlat`; gradients, shapes and blurs are drawn by the addon's paint and shader code. See [known differences](/docs/differences) for the small places the two runtimes differ. ## Color tokens Every color can be a token: write `$ember` instead of `#f2a24e`. Change the token and every layer that uses it follows, on every screen. Theme presets in the [starter kit](/kit) are sets of token values. ## Text Text layers have content, font, size, weight, color, alignment, line height, letter spacing, wrapping, uppercase, an outline and a shadow. In Godot they become a `Label` with `LabelSettings`. **Text styles** are shared typography (Display, Title, Button, Body…). A layer that uses a style follows it, so changing one style restyles every screen. Text can include data: `HP {player.hp} / {player.hpMax}` updates as the game sets data. See [Data and events](/docs/data). ## Fonts Envy has the whole Google Fonts catalog in its font picker. For the game, **embed** the fonts you use: the font files travel inside the document, and both runtimes use them with no setup. - On the web, the runtime registers each embedded font directly, so a strict content security policy can't block it. - In Godot, `EnvyUI` builds a `FontFile` for each embedded face. An entry in `EnvyUI.fonts` for the same family still wins. The Last Hunt embeds Jaini, Archivo at two widths and Spectral Italic. ## Shapes Ellipse, polygon, star, line and **path**. Draw paths with the pen tool (N): click for corners, drag for curves, then edit points. Closed paths are filled and stroked, open paths are stroked. Both runtimes draw the same geometry. --- # Components > Components, instances, variants, overrides and component properties in Envy. Build a button once and keep every copy on every screen in step. Source: https://envyui.com/docs/components A **component** is a board whose layers define a reusable piece: a button, a menu item, an item slot. An **instance** is a copy of it on a screen. Edit the component and every instance follows. ## Create one Select a layer and press **Ctrl Alt K**. The layer becomes a component board, and the original spot holds an instance of it. ## Variants Components that belong to the same **set** and differ in variant values are **variants**: Primary, Secondary, Ghost and Danger buttons, in Default, Focus, Pressed and Disabled, at three sizes. The Last Hunt's Button set has 48 variants. Switch an instance between them from the inspector. ## Properties Properties let an instance change what it's meant to change, in one field: | Type | What it does | |---|---| | Text | Replaces `{prop.Label}` in the component's text layers | | Toggle | Shows or hides target layers (a badge, a meta line, a glyph) | | Instance swap | Swaps a nested instance for another component (a different glyph or icon) | The Last Hunt's menu item has `Label`, `Meta` and `Show meta`. Its main menu sets them per item: Continue shows "Chapter III", New hunt hides its meta line. ## Overrides Anything else can be overridden per instance: a layer's text, fill, visibility, image, states, click actions or bindings. Overrides are stored on the instance by the path of the layer inside the component, and survive changes to the component. **Reset** clears them; **Detach** (Ctrl Alt B) turns the instance into plain layers. ## Button states Buttons have **hover**, **pressed** and **disabled** states, each a small patch: fill, border color, text color, opacity and scale. Keyboard and gamepad focus use the hover look, plus an optional focus ring. Preview each state on the canvas before any code exists. ## In the runtimes Both runtimes expand instances before drawing, with the same algorithm (`resolve.ts` on the web, `envy_resolve.gd` in Godot). Layers inside an instance get ids like `instanceId/sourceId`, which animations can target. --- # Data, events and lists > Bind Envy layers to game data, emit named events from buttons, run actions without code, preview scenarios and repeat a row for every item in an array. Source: https://envyui.com/docs/data Your game and its UI agree on two things: **data paths** the UI reads, and **event names** the UI sends. That's the whole contract. Re-export the UI as often as you like and game code doesn't change. ## Bindings Bind a layer property to a data path: | Property | Example | |---|---| | `text` | `player.name` | | `value`, `max` | Progress bars: `player.hp`, `player.hpMax` | | `visible` | `hud.showCrit` (prefix with `!` to negate: `!store.any`) | | `opacity` | `ui.fade` | | `disabled` | `!shop.canAfford` | Text layers can also use templates: `{player.hp} / {player.hpMax}`. A path the data doesn't have leaves the template visible, on purpose, so you notice. From the game: ```gdscript ui.set_data("player.hp", 48) ui.merge_data({"boss": {"hp": 22, "antlers": 2}}) ``` ```js ui.setData("player.hp", 48); ui.setData({ boss: { hp: 22, antlers: 2 } }); ``` ## Sample data and scenarios **Sample data** fills the canvas and preview before the game exists. **Scenarios** are named patches over it: Hunt start, Low health, Boss enraged. Pick one to see it on the canvas. The AI handoff renders every screen in every scenario, so the programmer sees the edge cases too. Runtimes never read scenarios. ## Events A button's click can **emit** an event with an optional payload. Game code listens for the name, never for a particular button: ```gdscript func _on_ui_event(event: String, payload: String) -> void: match event: "new_hunt": start_new_hunt() "buy": shop.buy(payload) ``` ```js ui.on("new_hunt", () => game.startHunt()); ui.on("*", (payload, event) => console.log(event, payload)); ``` ## Actions A click runs a list of actions in order, no code needed: | Action | Does | |---|---| | `emit` | Sends an event to the game | | `goto` | Shows another screen (with the screen's transition, or its own) | | `push` / `pop` | Opens a screen over this one, closes the top one | | `play` | Plays an animation | | `set` / `toggle` | Changes data, like `settings.subtitles` | The Last Hunt's menu items emit `new_hunt` and then go to Character select, so the prototype clicks through and the game still gets its event. ## Lists Bind a frame to an array and its **template** child is drawn once per item: ```json "list": { "path": "store.items", "template": "", "key": "id" } ``` Inside the template, `item.x` and `{item.x}` refer to the current item, and `{index}` is its position. An emit payload like `{item.id}` tells the game which item was clicked. Items with a `key` keep their layers when the list is reordered. Lists can nest. Preview a list empty, with a few items or with a hundred, using scenarios. --- # Animation, transitions and overlays > Keyframe animation in Envy with seven easing curves, screen transitions (fade, slide, scale) and overlay screens, identical in Godot and the browser. Source: https://envyui.com/docs/animation ## The timeline Open the timeline with **Alt T**. An animation belongs to a screen and has tracks of keyframes for a layer's `x`, `y`, `scale`, `rotation` and `opacity`. Values add to the layout: x and y move the layer, scale and opacity multiply, rotation adds. Turn on **record**, move a layer, and the timeline writes the keyframe for you. - **Autoplay** plays it when the screen opens (The Last Hunt's menu Intro). - **Loop** repeats it (the breathing "Press any button"). - Or start it from a button's **Play** action, or from game code: `ui.play("Hit")`. ## Easing Each key's ease shapes the segment after it: `linear`, `in`, `out`, `inOut`, `back`, `elastic`, `step`. The curves are the same functions in TypeScript and GDScript, so motion matches on both sides. ## Screen transitions A screen can say how it appears when you go to it: ```json "transition": { "type": "fade", "duration": 0.3 } ``` | Type | Effect | |---|---| | `fade` | The old screen fades out while the new one fades in | | `slide` | Both move in a direction; the new screen enters from the opposite edge | | `scale` | The new screen grows from 92% to 100% while fading in | A `goto` or `push` action can override it, and so can code: `ui.goto("12 Pause", { transition: { type: "none" } })`. The old screen stays mounted but takes no input until the new one has arrived. On the web, transitions are skipped for people who ask their system for reduced motion. ## Overlays `push` opens a screen over the current one: a pause menu over the HUD. Screens below keep their state, follow data and keep animating, but take no input until they're on top again. Focus moves into the overlay and comes back to the button that opened it. `pop` closes the top screen. The overlay's own background is what dims the screen below, so give it a semi-transparent fill. ```gdscript ui.push("12 Pause") ui.pop() ``` --- # Keyboard and gamepad focus > Make Envy menus playable with arrow keys, a d-pad or a stick. Automatic focus by position, explicit neighbours, initial focus and focus rings in Godot and the web. Source: https://envyui.com/docs/focus Every button can take keyboard and gamepad focus in both runtimes. With nothing set, arrows and the d-pad move to the nearest button in that direction. Set neighbours when the layout needs something else. ```json "focus": { "down": "