Game UI Scale Preview — Resolutions, Anchors, Safe Areas
See a panel or button where it will really be drawn: in a simulated screen at the resolution and aspect ratio you target, positioned by an anchor preset, next to its fractional-scale versions.
The preview draws your panel or button inside a simulated 1280 × 720 to 3840 × 2160 screen (4:3, 16:9, 16:10 or 21:9), places it with Godot-style anchors, overlays a 90 % title-safe or 93 % / 95 % action-safe area, and shows it at 1×, 1.25×, 1.5×, 1.75×, 2×, 2.5× and 3×: integer scales keep pixel art crisp, fractional ones soften it. The same Check stage measures Korean, English and Japanese labels against a box and computes the WCAG contrast ratio (white on black is 21:1). It is arithmetic in the browser, not an engine layout pass.

- Nine-sliceMeasured· Pillow
- Bitmap fontMeasured· independent .fnt parser
- Button statesMeasured· Pillow
- Engine importUNVERIFIED
Scale, safe areas and contrast in numbers
At an integer scale every source pixel becomes a whole block of screen pixels: at 2× a 16 px icon is 32 px and each pixel is 2 × 2. At 1.5× two source pixels have to cover three screen pixels, so with nearest filtering the columns alternate between 1 and 2 px wide, and with linear filtering neighbouring colours blend into a soft edge; the preview draws fractional scales smoothed. Godot 4.2 and later have a stretch scale mode Integer that rounds the scale factor down (2.5 becomes 2), which its documentation recommends for pixel-art games together with the viewport stretch mode.
Safe areas come from television: EBU R 95 (version 1.1, 2017) keeps essential action 3.5 % and graphics 5 % in from every edge, which is the 93 % action-safe and 90 % title-safe rectangle the preview draws; the 95 % preset is an extra, looser option and not part of R 95. Phones and consoles report their own safe area at run time instead (Unity Screen.safeArea in pixels, Godot DisplayServer.get_display_safe_area()), so those are entered here as custom insets rather than shipped as presets.
Contrast uses the WCAG 2.2 formula: each sRGB channel is linearised, relative luminance is L = 0.2126 R + 0.7152 G + 0.0722 B, and the ratio is (L1 + 0.05) ÷ (L2 + 0.05) with the lighter colour first, from 1:1 to 21:1. Success Criterion 1.4.3 asks for 4.5:1 for text and 3:1 for large text (18 pt, or 14 pt bold; the preview uses 24 px and 18.66 px), 1.4.6 for 7:1, and 1.4.11 for 3:1 on the visual parts of UI components. A ratio describes two flat colours; text over artwork has a different ratio at every pixel.
- Integer scale
- A scale factor that is a whole number, so each source pixel covers an exact block of screen pixels.
- Title-safe (graphics safe)
- The inner 90 % of the frame: 5 % margin on every side in EBU R 95.
- Action-safe
- The inner 93 % of the frame: 3.5 % margin on every side in EBU R 95.
- Relative luminance
- The brightness of a colour from 0 (black) to 1 (white) after linearising sRGB, used for the contrast ratio.
- Anchor
- Where an element is pinned in its parent, as four numbers from 0 to 1 (left, top, right, bottom).
Example: the numbers the Check stage computes
Every value below comes from the same functions the preview uses:
contrast L = 0.2126 R + 0.7152 G + 0.0722 B (sRGB linearised first)
white/black (1.00 + 0.05) / (0.00 + 0.05) = 21.0 : 1
#767676 on white L = 0.1812 → 1.05 / 0.2312 = 4.54 : 1 passes 4.5:1 by 0.04
white on #3182f6 L = 0.2327 → 1.05 / 0.2827 = 3.71 : 1 UI 3:1 and large text: yes · 16 px text AA: no
safe 1920×1080 title-safe 90 % → x 96, y 54, 1728 × 972 action-safe 93 % → x 67, y 38, 1786 × 1004
anchor bottom-right, 300 × 80 button, 32 px margin → x = 1920 − 300 − 32 = 1588, y = 1080 − 80 − 32 = 968
scale base 640 × 360 → 1920 × 1080 is exactly 3×; 1366 × 768 is 2.13× → Integer mode draws 2× (1280 × 720)The default colours of the contrast tab are white on #3182f6: fine for a large label or an icon, too low for 16 px body text. At 1920 × 1080 EBU R 95 itself gives 67 px as 3.5 % of the width, the same number the preview draws.
What it does
- Screens 1280×720 to 3840×2160 and 4:3, 16:9, 16:10, 21:9; anchor presets with Godot 4's anchor numbers.
- Safe areas: 90 % title-safe, 93 %/95 % action-safe (the broadcast convention, labelled as such) and custom insets.
- Scale strip 1× to 3×: integer scales marked crisp.
- Localisation overflow per language with measured widths, and the WCAG contrast formula.
How to do it
- Drop the UI element.
- Pick a screen, anchor and safe area.
- Check the scales, text overflow and contrast tabs.
- Adjust the art or layout accordingly.
Check the result
- Scale strip: pixel art at 2× and 3× should keep hard edges; if the 1.5× version is unacceptable, plan for integer steps or a base resolution that divides your target screens.
- Sizes tab: the element's rectangle stays inside the safe-area numbers on every aspect ratio you ship, including 21:9.
- Localisation tab: every language reads "fits" (or "wraps" where your UI allows wrapping); then repeat with your game font, because the preview measures a browser font.
- Contrast tab: body-text labels reach at least 4.5:1, large labels and icons at least 3:1.
Troubleshooting
| Symptom | Likely cause | How to check | Fix |
|---|---|---|---|
| Pixel-art UI looks blurry on some monitors | The UI is drawn at a fractional scale (for example 2.13× on 1366 × 768) with linear filtering | Compare the 1.5× and 2× cells of the scale strip | In Godot use the viewport stretch mode with the Integer scale mode and Nearest filtering; see blurry pixel art in Godot |
| A label fits in the preview but overflows in the game | The preview measured the text with a browser font; the game font has other widths | Measure the same string with the game font (the Font stage builds one) | Leave spare width, allow wrapping, or shorten the translation |
| Korean or Japanese breaks in the middle of a word | Text without spaces is broken per character by the preview's greedy line breaker | Look at where the line ends in the Localisation tab | Accept per-character breaks, or insert explicit breaks; engines have their own line-breaking rules |
| An element leaves the safe area only on 21:9 | It is anchored to a screen edge, and the wider frame moves that edge | Switch the aspect to 21:9 with the safe area on | Add a margin at least as large as the safe-area inset, or anchor inside a safe-area container |
| The contrast passes but the label is hard to read over art | The ratio compares two flat colours, and the art behind the text varies | Measure the lightest and darkest pixels behind the text | Put a backing panel, an outline or a shadow behind the label |
Other ways to do it
| Option | When it is the better choice |
|---|---|
| Run the scene in the engine at each target resolution | You need the real layout: containers, size flags, theme margins and the engine's own text rendering. |
| Pixel-perfect checker | You have a screenshot from the game and want to know whether it was scaled by a whole number. |
Outputs and how each was checked
"Measured" means the downloaded file was opened again outside the page (Pillow, numpy or an independent parser) and the property was measured. Loading these files in a game engine was not tested.
| Target | Files | Check |
|---|---|---|
| Nine-slice | nine-slice.json · previews · SETUP.md | Measured PillowEvery resized preview keeps all four corner blocks unscaled: the same bytes in Chromium; in Firefox semi-transparent pixels can shift by one alpha-rounding step. 0 gaps, 0 overlaps. |
| Bitmap font | font.png · font.fnt (BMFont text) · font.json | Measured independent .fnt parserThe .fnt was read back by a parser written separately in the test: header and every glyph rect match. |
| Button states | states/*.png · strip · states.json | Measured PillowNormal equals the source (byte for byte in Chromium; in Firefox semi-transparent colour within one alpha-rounding step); every rect in states.json cuts exactly its state out of the strip. |
| Engine import | Godot NinePatchRect · Unity Sprite border · BMFont import | UNVERIFIEDNot run in Godot or Unity. |
Evidence
Nine-slice corners unscaled at 100×40, 300×80, 800×200 and 420×120; border suggestion 6·6·6·6 from a run of 12 identical columns; button states: normal equal to the source, disabled grey at alpha 128, focus ring exact; .fnt glyph records equal the JSON; missing glyphs from a .po listed exactly; black on white measured 21:1. Images pass through a browser canvas: Chromium returns the same bytes; Firefox rounds the colour of semi-transparent pixels to one premultiplied step (measured on a Kenney button: up to 4 levels at alpha 63), while alpha and opaque pixels stay exact.
Limits
- No engine layout pass: containers, size flags and theme margins are not modelled, only the anchor arithmetic.
- No console or phone presets: those safe areas are device-specific and go in custom insets.
- Text widths come from a browser font, not your game font.
- No kerning pairs, one font page, no MSDF; the SDF output is Beta.
- A TTF's cmap is not parsed, so the missing-glyph check needs a .fnt or the Lab's own font.
- One atlas page; touching elements count as one.
- Rendering inside Godot or Unity was not run: the engine numbers (patch margins, L-B-R-T border) are documented, not engine-tested.
Versions and sources
Measured in Chromium for docs/UI-LAB.md: title-safe at 3840 × 2160 is 192, 108 · 3456 × 1944; a top-left anchor with a 64 px margin lands at 64, 64; a 120 px box makes an English label overflow while the Korean sample fits; black on white is exactly 21:1 and #767676 on white 4.54:1. The example values were recomputed with the same functions. Safe-area percentages follow EBU R 95, the contrast formula WCAG 2.2, the integer scale mode the Godot 4.7 documentation.
Official documentation used
Questions
Is this an engine layout pass?
No. It is the anchor arithmetic (Godot's four anchor numbers, which Unity presets share) simulated in the browser, and it says so.
Are console safe areas included?
No. Console and phone cut-out values are device-specific and are not shipped as presets; use custom insets.
Is it free, and do I need an account?
Everything on these pages is free and works without an account. There is no watermark and no export that is held back.
Are my sprites uploaded?
No. The Studio decodes, packs and exports in this browser tab. Projects are autosaved in the browser (IndexedDB) and can be saved as a .nerulio file.
Does it need Aseprite or an engine plugin installed?
No. .aseprite files are read and written in the browser, and each engine bundle carries what that engine needs (for Godot, the import settings and scene; for Unity and the Godot TileSet, a small editor script).