---
name: muse-skins
description: Create, edit, preview and apply skins for the Muse macOS music player with the `muse` CLI. Use when the user asks for a new Muse look, a skin "like" a picture or description, or changes to a Muse skin's layout, colors or controls.
---

# Making Muse skins

Muse skins are JSON files (`skin.json`) that set the player's colors, fonts and full layout: where
the cover, title, buttons and progress bar go, their shapes, and what appears on hover. You build
one, check it, apply it, then look at a screenshot and improve it.

## Tools

The `muse` CLI. The installer links it to `~/.local/bin/muse`. If it isn't on PATH, use
`~/.local/bin/muse`, `/Applications/Muse.app/Contents/Helpers/muse` (or `~/Applications/…`), or
`build/Muse.app/Contents/Helpers/muse` in the Muse repository. Muse not installed yet? Follow
https://tinymuse.app/install.md first.

```bash
muse skin list --json                       # installed skins, the current one, file paths
muse skin reference                         # short list of node types and their properties (start here)
muse skin schema                            # full JSON Schema (long; use for exact types)
muse skin new "<Name>" --from <skin>        # copy a skin into the user folder (prints the path)
muse skin validate "<Name or folder>"       # exit 0 if valid; otherwise "path: problem" lines
muse skin snapshot out.png --skin <Name> [--mini | --menubar]   # preview WITHOUT switching to it
muse skin apply "<Name or folder>"          # switch Muse to it (a path installs it first)
muse play | pause | next                    # to see the playing state (spin, tone arm, reels)
```

User skins live in `~/Library/Application Support/Muse/Skins/<Name>/skin.json`, with any images
beside it.

Shapes agents most often guess wrong (check `muse skin schema` for anything else):
`"background": { "gradient": ["#3a3a3a", "#111"], "angle": 90 }` for a gradient (not
`"type": "linear"`), `{ "material": "translucent" }` for blur, `{ "artwork": { "blur": 40 } }` for
blurred cover art. Midnight, Paper and Mono only recolour Default; start from Default, Pill, Card,
Turntable or Tape when you want a different layout.

## Workflow

1. **Start from the closest built-in skin.** Run `muse skin list --json` and read the `file` of the
   most similar one:
   - **Default**: standard window, compact rows
   - **Pill**: overlapping round cover and pill, using negative `hstack` spacing
   - **Card**: artwork filling a card with controls over it, using a `zstack` and gradient scrim
   - **Turntable**: `vinyl` plus `tonearm` placed with `offset`
   - **Tape**: `cassette` beside a control column

   Then run `muse skin new "<Name>" --from <that skin>`.
2. **Edit `skin.json`.** Check property names against `muse skin schema` when unsure. Keep the root
   node's `width` explicit.
3. **Run `muse skin validate "<folder>"`** and fix every reported path until it prints ✓.
4. **Preview it** with `muse skin snapshot <png> --skin "<Name>"` and **look at the image**. This
   doesn't change the user's current skin. Check:
   - Text isn't cut off.
   - Nothing overlaps by accident.
   - Controls are reachable and there's contrast in light and dark mode. If `appearance` isn't
     forced, snapshot both.
   - The mini player works, using `--mini`. Without a `layout.mini`, mini mode shows just the
     cover art in a plain square, so add a `mini` layout if the skin should keep its look there.

   The snapshot shows the live state: the playlist appears if the user has it open, and spinning
   parts move if music is playing.
5. **Iterate** until it looks right. Don't start playback or change the volume unless the user
   agrees; if music is already playing you'll see the playing state anyway.
6. **Apply it only if the user wants it** (`muse skin apply "<Name>"`), then tell them where the
   file is.

If shell commands can't write to `~/Library/Application Support/Muse/Skins` (for example in a
sandboxed agent shell), edit the files with your file-editing tool instead.

## Rules that matter

- **Window:** a skin with `layout` gets a transparent window, so the root node must paint its own
  `background` and shape. The window ends exactly at the layout's edges, so don't add shadows
  around the outside (they would be cut off). Keep the look flat.
- **Buttons:** include every feature, as the built-in skins do:
  - transport (`previous`, `play`, `next`);
  - `shuffle` and `repeat`;
  - `volume` or `mute`;
  - `playlist` with a `playlist` node;
  - `mini` with a `layout.mini` that has a way back;
  - `add`, `pin` and `menubar` (editing is on the cover: click or right-click it).

  Keep spacing tight; small icon buttons (size 10–11) work for the extras.
- **Width:** the root's `width` is its starting width. Add `minWidth`/`maxWidth` so it can be
  resized.
- **Colors:** use `"#RRGGBB"`, `"#RRGGBBAA"`, `{ "light", "dark" }`, or theme names like `"accent"`
  and `"textSecondary"`.
- **Sizes:** use points or `"fill"`. `padding` is `n`, `[v, h]`, or `[top, right, bottom, left]`.
- **Empty fields:** song fields can be empty (many files have no artist or album), and an empty
  text line collapses. Set `placeholder` to keep the line. The `artist` node does this by default
  only when nothing is loaded.
- **Volume icons:** the speaker icons on a `volume` node take its `color`; set `"icons": false` to
  hide them.
- **Overlaps:** use negative `spacing` in an `hstack`/`vstack`, or `offset` inside a `zstack`.
  Raise `zIndex` for the one on top.
- **Conditional nodes:** `when` shows a node only in some states, e.g. `"hover"`, `"playing"`,
  `"!hasArtwork"`, `"playlist"`, `"hover&playing"`.
- **Playlist:** put it inside the player's root panel, not as a separate card. Add a `divider` and
  a `playlist` (with a `height`), both `"when": "playlist"`, plus a `playlist` button. It slides in
  from the bottom automatically.
- **Borders:** no `border` on the outer panel. The window ends at the panel's edge, so half of an
  outer border would be cut off.
- **Variables:** put repeated sizes and colors in `"vars"` and use them as `"$name"`. Keep one
  spacing scale (for example gap 8, padding 14, icon sizes 10/11/12) across the whole skin.
- **Images:** files referenced by `image`, `background.image` or `icon` must sit next to
  `skin.json`. Otherwise prefer SF Symbols and drawn shapes (`spacer` with a `background` and
  `shape`).
- **Branding:** don't copy trademarked brand names or logos; describe the style instead.

## Minimal layout to adapt

```json
{
  "version": 2,
  "name": "Example",
  "appearance": "dark",
  "colors": { "accent": "#FF9F0A" },
  "layout": {
    "main": {
      "type": "vstack", "width": 320, "spacing": 10, "padding": 16,
      "background": "#1C1C1E", "cornerRadius": 22,
      "children": [
        { "type": "hstack", "spacing": 12, "children": [
          { "type": "cover", "width": 56, "height": 56, "cornerRadius": 10 },
          { "type": "vstack", "alignment": "leading", "spacing": 2, "maxWidth": "fill", "children": [
            { "type": "title" }, { "type": "artist" }
          ] },
          { "type": "button", "action": "mini", "size": 12 }
        ] },
        { "type": "progress", "thickness": 4 },
        { "type": "hstack", "children": [
          { "type": "button", "action": "previous", "size": 16 },
          { "type": "spacer" },
          { "type": "button", "action": "play", "style": "filled", "size": 18 },
          { "type": "spacer" },
          { "type": "button", "action": "next", "size": 16 }
        ] }
      ]
    }
  }
}
```
