Skin format (skin.json)
A Terminal Candy skin is just a folder with two files. This page is the full reference — everything the Skin Builder writes, documented so you can hand-author or generate skins.
my-skin/
├── skin.json ← manifest (required)
└── frame@2x.png ← device image (required)
Install location: ~/Library/Application Support/Terminal Candy/skins/
The @2x suffix matters — macOS uses it to infer point-vs-pixel density. An 800×1300-pixel image is treated as a 400×650-point device.
The PNG
- Transparent background — everything outside the device body should be alpha 0. Terminal Candy does pixel-perfect hit-testing, so transparent areas pass clicks through to apps behind the window.
- Transparent screen cutout (optional but recommended) — if the screen area is already transparent, the terminal shows through cleanly. If it’s opaque, Terminal Candy punches a hole at the
viewportcoordinates at render time. - @2x density — export at 2× the logical size.
- Portrait orientation tends to look best, but landscape works.
Example manifest
{
"name": "My Device",
"author": "Your Name",
"version": "1.0",
"category": "retro",
"frame": {
"image": "frame@2x.png",
"width": 400,
"height": 650
},
"viewport": {
"x": 50,
"y": 80,
"width": 300,
"height": 180,
"cornerRadius": 6
},
"palette": {
"foreground": "#00FF41",
"background": "#050505",
"cursor": "#00FF41"
},
"buttons": [
{ "label": "MSG", "x": 40, "y": 340, "width": 70, "height": 50 },
{ "label": "", "x": 120, "y": 340, "width": 70, "height": 50 }
],
"keys": [
{ "key": "q", "x": 20, "y": 420, "width": 32, "height": 32 },
{ "key": "space", "x": 120, "y": 500, "width": 180, "height": 30 },
{ "key": "shift", "x": 20, "y": 470, "width": 48, "height": 30, "shape": "circle" }
],
"effects": {
"scanlines": true,
"scanlineIntensity": 0.3,
"glow": true,
"glowIntensity": 0.4,
"curvature": false,
"curvatureAmount": 0.15
}
}
Coordinate system
All coordinates are logical points — frame.width / frame.height define the point space, origin top-left. viewport.y = 80 means 80 points from the top of the device, not the window.
Field reference
| Field | Required | Notes |
|---|---|---|
name | yes | Shown in the skin picker |
author | no | — |
version | no | Your own versioning |
category | no | Free-form string (e.g. retro, military) |
frame.image | yes | PNG path, relative to the skin folder |
frame.width/height | yes | Logical points (pixels ÷ 2 for @2x) |
viewport.x/y/width/height | yes | Rect where the terminal renders |
viewport.cornerRadius | no | Default 4. Clips terminal corners. |
viewport.corners | no | Four per-corner offsets (TL, TR, BR, BL). Non-zero values pull corners off the rect, producing an arbitrary quadrilateral for angled/tilted screens. |
viewport.cornerRadii | no | Four per-corner radii (TL, TR, BR, BL), 0–20 pt. Omitted = uniform cornerRadius. |
palette.foreground | yes | Hex — terminal text color |
palette.background | yes | Hex — terminal background |
palette.cursor | no | Hex — defaults to foreground |
buttons[] | no | 0–4 clickable rects. More than 4 are ignored. |
buttons[].label | yes | Empty string = button is painted into the PNG; Terminal Candy only tracks clicks |
keys[] | no | Reactive keys — each pulses when the matching key is pressed |
keys[].key | yes | Lowercase char ("w") or special: space, return, tab, delete, escape, shift, cmd, option, ctrl, up, down, left, right |
keys[].shape | no | "rect" (default) or "circle" |
effects | no | Default effects when the skin loads; users can override in Settings |
Tips
- The screen area. If your PNG has a transparent cutout at the viewport, Terminal Candy detects it and skips the hole-punch. Opaque there? It clears the alpha within
viewportat render time. - Buttons. The 4 buttons map to the 4 panel slots. Painted-in buttons → empty
labelso no chrome is drawn over your art. - Alpha matters. Corners must be alpha 0, not near-black matte — hit-testing is pixel-perfect.
- Effects defaults. If your artwork already has a CRT-glass highlight painted in, skip built-in
curvature.
Sharing
Publish straight from the Skin Builder with Share to Community… — details in the Skin Builder guide. Or keep it local: any valid folder dropped into the skins directory just works.