Quick start

Let’s build the smallest possible widget — one that shows the current track. It takes about five minutes.

1. Create the bundle folder

Make a new folder named Hello.nepget. (On macOS, the .nepget extension turns it into an installable bundle.)

2. Add a manifest

Create manifest.json inside the folder:

{
  "manifestVersion": 1,
  "id": "com.example.hello",
  "name": "Hello",
  "version": "1.0.0",
  "entry": "index.html",
  "defaultSize": { "width": 280, "height": 120 },
  "permissions": ["artwork"]
}

Every widget needs manifestVersion, a unique id (reverse-domain style), a name, a version, an entry HTML file, and a defaultSize. See the full manifest reference.

3. Add the HTML

Create index.html:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <style>
      html, body {
        height: 100%;
        margin: 0;
        background: transparent;
        font-family: -apple-system, system-ui, sans-serif;
      }
      .w {
        height: 100%;
        display: grid;
        place-items: center;
        color: #fff;
        background: rgba(0, 0, 0, 0.5);
        border-radius: 16px;
      }
    </style>
  </head>
  <body>
    <div class="w"><div id="t">Nothing playing</div></div>
    <script>
      NepTunes.on('statechange', (state) => {
        document.getElementById('t').textContent = state.track
          ? `${state.track.title} — ${state.track.artist}`
          : 'Nothing playing';
      });
    </script>
  </body>
</html>

The statechange event fires whenever the track or playback state changes. state.track is null when nothing is playing — always handle that case.

4. Install it

Double-click Hello.nepget. It installs to ~/Library/Application Support/NepTunes/Widgets/ and appears on your desktop. Start playing something and the text updates live.

5. Debug it

Open Safari → DevelopNepTunes WidgetOpen Web Inspector to inspect the DOM, tweak CSS, and watch the console. See Debugging for the full workflow.

A note on shadows

Widgets render on a transparent background. If you want a drop shadow around your widget, give the body some padding so the shadow has room, and keep the background transparent:

html, body { height: 100%; width: 100%; background: transparent; overflow: visible; }
body { padding: 20px; }            /* room for the shadow */
.widget {
  height: 100%; width: 100%;
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}

← Introductionmanifest.json reference →