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 → Develop → NepTunes Widget → Open 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);
}