Introduction

NepTunes web widgets are small, always-on panels that sit on your desktop and react to whatever you’re playing in Apple Music or Spotify. A widget is just HTML, CSS and JavaScript — if you can build a web page, you can build a widget.

Widgets need NepTunes 4, which requires macOS 26 or later.

Each widget runs in its own transparent WebView and talks to the app through a single global object, window.NepTunes, which exposes the current track, playback state, and actions like play, pause and love.

Three things are worth knowing before you start, because each shapes how you work:

  • You can build one with an AI assistant. These docs are served as an MCP server, so Claude, Cursor, Codex or OpenCode can read the whole reference and the manifest schema directly and write the widget with you. Point your editor at it and describe what you want — see Build with an AI assistant.
  • Every widget is signed. Signing is not a publishing formality at the end; it is part of building, and it is one downloaded script and one command. See signing, below.
  • A widget cannot reach the network. It runs in a sandbox with no remote access at all, so bundle your assets rather than linking a CDN. See Security & the sandbox.

The .nepget bundle

A widget ships as a .nepget bundle — a folder with a specific extension. At minimum it contains:

MyWidget.nepget/
├── manifest.json   # metadata, size, permissions, settings
├── index.html      # the entry point (set via "entry")
├── styles.css      # your styles (optional)
├── script.js       # your logic (optional)
├── preview.jpg     # a preview image for the picker (optional)
└── bundle.sig      # the author's signature over every file above

The manifest.json describes the widget — its name, version, default size, the permissions it needs, and any user-configurable settings. The entry HTML file is what NepTunes loads.

Signing is part of building

bundle.sig is the last file in that listing and the easiest one to leave until too late. It is an Ed25519 signature over every other file in the bundle, and NepTunes checks it every time the widget installs and every time it loads — not only when you publish.

Practically, that means:

  • Sign with widget-tools.mjs — one Node script, built-ins only, nothing to install.
  • Sign last. Any edit after signing invalidates the signature, and the app will refuse the bundle rather than load a stale one.
  • Keep your key. The first signed version of a widget id pins its author key: later versions should be signed by the same key, and an unsigned update is refused. Signing with a different key still works, but turns every user’s update into a re-consent prompt.
curl -O https://neptunesmac.app/widget-tools.mjs
node widget-tools.mjs keygen my-widget-author        # once, ever
node widget-tools.mjs embed-sign MyWidget.nepget --key .keys/my-widget-author.pem
node widget-tools.mjs embed-verify MyWidget.nepget   # exactly what the app will do

Full detail, including the update feed and the downgrade ratchet, is in Updates & signing.

Installing a widget

Double-click a .nepget bundle and NepTunes installs it. NepTunes is sandboxed, so widgets live in its app-group container rather than in Application Support:

~/Library/Group Containers/group.pl.micropixels.NepTunes/Widgets/

Once installed, a widget appears on your desktop where you can position and (if it allows) resize it.

Every install is signature-checked: if a bundle carries a bundle.sig, NepTunes verifies it against the bundle’s own contents before installing — and once a widget has installed signed, an unsigned version of it is refused. Widgets from the gallery additionally receive signed, verified updates, each one confirmed by you. See Updates & signing.

Desktop widgets require NepTunes Pro

The on-screen web-widget system is part of NepTunes Pro. Anyone can build and share widgets, and downloads from the gallery are free — running them on your desktop unlocks with Pro inside the app.

Quick start →