jukette
A white-label jukebox custom element exposed as <jukette-player>.
Jukette v0.9.0
By Rémino Rem
https://remino.net/
Docs | Code Repo | npm Package
Installation
HTML (CDN)
Register the custom element automatically from a CDN:
<script src="https://unpkg.com/jukette"></script>Mirrors:
Use a pinned version in production:
<script src="https://unpkg.com/jukette@0.9.0"></script>If you want the API instead of auto-registration, import the ES module directly:
<script type="module">
import { defineElement } from 'https://unpkg.com/jukette@0.9.0/dist/jukette.mjs'
defineElement()
</script>npm
Install the convenience package first:
npm install juketteThen register the custom element automatically:
import 'jukette/auto'Or import the explicit API:
import { defineElement } from 'jukette'
defineElement()TypeScript declarations are included with the package.
Optional modular packages:
npm install jukette @remino/jukette-midi @remino/jukette-soundcloudjukette: convenience package with@remino/jukette-coreand@remino/jukette-audio.@remino/jukette-core: backendless player infrastructure.@remino/jukette-audioand@remino/jukette-audio/auto: browser-native audio backend.@remino/jukette-midiand@remino/jukette-midi/auto: optional MIDI backend.@remino/jukette-soundcloudand@remino/jukette-soundcloud/auto: optional SoundCloud backend.
Typical addon usage:
import 'jukette/auto'
import '@remino/jukette-midi/auto'
import '@remino/jukette-soundcloud/auto'Direct download
Download the package tarball or individual files from npm/CDN:
- https://www.npmjs.com/package/jukette
- https://unpkg.com/jukette/dist/
- https://cdn.jsdelivr.net/npm/jukette/dist/
The browser-ready auto-registration file is dist/jukette-auto.min.js.
Distribution files:
dist/jukette.mjs: ES module library API.dist/jukette.cjs: CommonJS library API.dist/jukette-auto.mjs: ES module auto-registration entry.dist/jukette-auto.cjs: CommonJS auto-registration entry.dist/jukette-auto.min.js: minified classic browser auto-registration.
Usage
After registration, use the element with a single source:
<jukette-player src="/audio/theme.mp3"></jukette-player>Or pass a playlist with child track elements:
<jukette-player preload-metadata prefer-media-metadata>
<jukette-track
title="Theme"
artist="Local"
src="/audio/theme.mp3"
></jukette-track>
<jukette-track
title="Sketch"
src="/midi/sketch.mid"
type="midi"
></jukette-track>
<jukette-track
title="Flickermood"
artist="Forss"
src="https://soundcloud.com/forss/flickermood"
type="soundcloud"
preload
></jukette-track>
<jukette-track
title="Reprise"
artist="Local"
src="/audio/reprise.ogg"
></jukette-track>
</jukette-player>Changing the selected track prepares it for playback but does not start it automatically. Jukette enables play, seek, and time controls once the selected track is ready.
The default jukette entry includes browser-native audio support only. Import addons for other track types:
import 'jukette/auto'
import '@remino/jukette-midi/auto'
import '@remino/jukette-soundcloud/auto'Playlist
Use direct <jukette-track> children for authored HTML. Browser HTML requires explicit closing tags, so write <jukette-track></jukette-track> rather than a self-closing tag.
For generated markup or compatibility with older usage, the playlist attribute also accepts JSON. Each item can be either a URL string or a track object.
[
"/audio/one.mp3",
{
"title": "Two",
"artist": "Example",
"src": "/audio/two.ogg",
"type": "audio",
"preload": true,
"preferMediaMetadata": false
}
]When JSON parsing fails, Jukette treats the attribute as a newline-separated URL list.
For remote JSON, point playlist-src at an endpoint or static file that returns the same JSON array or object shape accepted by the playlist attribute.
<jukette-player playlist-src="/audio/playlist.json"></jukette-player>Track sources are resolved in this order:
player.playlistset from JavaScript.- Direct
<jukette-track>children. playlistattribute JSON or newline list.playlist-srcremote JSON.- Single
srcattribute.
Tracks
<jukette-track> attributes and track object fields:
src: required URL for a local audio file, local MIDI file, or public SoundCloud track URL whentype="soundcloud"is used.title: optional display title.artist: optional display artist.type: optional backend-owned track type such asaudio,midi, orsoundcloud.start-at/startAt: optional per-track start offset in seconds. Accepts fractional seconds.preloadattribute /preloadobject field: optional per-track playback preparation preference.prefer-media-metadata/preferMediaMetadata: optional per-track override for the player’s media metadata preference.show-source-link/showSourceLink: optional per-track override for whether the selected track should expose a source-page link in the header.
If type is omitted, Jukette treats .mid / .midi sources as midi. Everything else defaults to audio.
In practice, the convenience jukette package registers the browser-native audio backend for you, so tracks without an explicit type normally behave as audio tracks unless their source looks like MIDI.
MIDI playback uses @tonejs/midi for parsing and a compact Tone.js synth for browser playback. It is intentionally simple and suitable for local MIDI previews, not a full General MIDI instrument set.
SoundCloud playback is available through the optional @remino/jukette-soundcloud addon. Selecting a SoundCloud track prepares the hidden widget and oEmbed metadata first, then enables Play once the widget is ready to accept playback calls. Player-level preload-metadata does not prepare SoundCloud tracks by itself; use per-track preload when you want a SoundCloud widget prepared before selection, and prefer-media-metadata when you want authored labels to yield to fetched SoundCloud metadata.
If a selected track type has no registered backend, Jukette leaves the track selected, keeps playback controls disabled, and surfaces that the track type is unavailable.
API
Each element exposes:
const player = document.querySelector('jukette-player')
player.play()
player.pause()
player.toggle()
player.seek(30)
player.currentTime = 30
console.log(player.currentTime)
console.log(player.currentTrack)
console.log(player.currentTrackIndex)
console.log(player.totalTracks)
player.playlist = [{ title: 'Track', src: '/track.mp3' }]
player.preloadMetadata = true
player.preferMediaMetadata = true
player.showTrackSelect = false
player.showSourceLink = true
player.midiOscillator = 'sine'Core and addon imports:
import { defineElement } from '@remino/jukette-core'
import { register as registerAudio } from '@remino/jukette-audio'
import { register as registerMidi } from '@remino/jukette-midi'
import { register as registerSoundCloud } from '@remino/jukette-soundcloud'
registerAudio()
registerMidi()
registerSoundCloud()
defineElement()Use the preload-metadata attribute or preloadMetadata property to discover playlist durations before tracks are played. Jukette preloads metadata for registered backends that provide preload hooks, including browser-native audio and local MIDI. SoundCloud stays opt-in here: player-level preload-metadata alone does not fetch or prepare SoundCloud tracks.
Use currentTime to read the current playback position in seconds. Assigning to currentTime seeks, matching native media element behavior.
Use currentTrack, currentTrackIndex, and totalTracks to inspect the track selection state.
Selecting a track prepares it and resets playback to the start of that track. Playback begins only after an explicit play() call or a user press on the play button.
If the selected track backend is unavailable, Jukette keeps the selected track visible but leaves play, seek, and time controls disabled.
Use prefer-media-metadata or preferMediaMetadata to let readable media-file tags override authored track titles and artists. Jukette currently reads MP3 ID3 TIT2 title and TPE1 artist tags, plus MIDI track/sequence names as titles. MIDI artists stay authored-only. Authored values stay in place when tags are missing, unreadable, or unsupported.
Direct <jukette-track> children and JavaScript track objects can override the player-level preference per track. Use prefer-media-metadata or preferMediaMetadata: true to force metadata display for that track, use prefer-media-metadata="false" or preferMediaMetadata: false to force authored display values, or omit it to inherit the player setting.
Use preload or preload: true to ask Jukette to prepare a track for playback when possible. The flag is track-local and does not change media metadata preloading. For SoundCloud tracks, it also opts that track into early widget preparation before the user selects it.
Use midi-oscillator or midiOscillator to choose the Tone.js MIDI preview oscillator. Supported values are auto, sine, square, sawtooth, and triangle. auto is the default and maps MIDI program changes to a simple preview timbre; invalid values fall back to auto. The property matters only when the MIDI addon is registered.
Use display-marquee or displayMarquee to control how the merged header display scrolls. Supported values are overflow, always, and never. overflow is the default and scrolls only when the text overflows.
Use show-track-select or showTrackSelect to control whether the track selector row is shown. The default is on. Set show-track-select="false" or player.showTrackSelect = false to hide it, and omit the attribute or set it to true to show it.
Use show-source-link or showSourceLink to expose a narrow source-page link beside the header display for the selected track. The default is off. When enabled, Jukette uses the selected track’s src as the link target. Direct <jukette-track> children and JavaScript track objects can override the player setting per track with show-source-link, show-source-link="false", showSourceLink: true, or showSourceLink: false.
Jukette dispatches bubbling composed custom events from the <jukette-player> host:
jukette:playjukette:pausejukette:seekjukette:endedjukette:trackchange
Each event includes event.detail with the current track, tracks, index, type, currentTime, duration, and playing.
Styling
Jukette keeps the default UI basic on purpose. It uses inherited text color and font, a single border, and native range controls. Style the host element first:
jukette-player {
color: #111;
font:
1rem/1.4 system-ui,
sans-serif;
max-inline-size: 36rem;
--jukette-control-size: 2.25rem;
}The host supports these stable styling inputs:
color: inherited by text, borders, buttons, and range accents.font: inherited by labels, buttons, and the track selector.--jukette-control-size: controls the square play button size. Defaults to2em.inline-size,max-inline-size,margin, and other normal layout properties onjukette-player.
Use host attributes for state-specific styling:
jukette-player[data-kind='midi'] {
color: #164e63;
}Range inputs use accent-color: currentColor inside the shadow DOM, so changing the host color changes the seek accent in browsers that support native range accent styling.
For deeper styling, Jukette exposes stable ::part() hooks:
- Layout:
player,track,seek,time,controls. - Track display:
display. - Controls:
button,play-button,seek-input,track-select.
jukette-player::part(player) {
border: 0;
padding: 0;
}
jukette-player::part(play-button) {
border-radius: 999px;
}
jukette-player::part(track-select) {
font-size: 0.95rem;
}Roadmap
Forward-looking work lives in ROADMAP.md. For now, that covers the planned SoundCloud addon direction without expanding jukette core again.
Development
npm install
npm run dev
npm run buildThis repository is an npm workspaces monorepo:
packages/jukette: publishesjukette.packages/core: publishes@remino/jukette-core.packages/audio: publishes@remino/jukette-audio.packages/midi: publishes@remino/jukette-midi.apps/docs: Astro docs and playground site.
Root scripts orchestrate builds in dependency order. Use npm run build, npm run typecheck, and npm test from the repo root.
Common root shortcuts:
npm run devornpm run dev:docs: start the Astro docs site.npm run build:packages: build the publishable packages only.npm run build:docs: build the docs app only.npm run preview:docs: preview the built docs app.
Release
Release automation is available through release-it. A release runs checks, bumps the root and publishable workspace package versions in lockstep, builds the workspace packages, dry-runs publish order, publishes the scoped packages first, publishes jukette last, creates the GitHub release, and then publishes docs:
npm run release:dry-run
npm run releaseIf docs publishing fails after the package release, rerun it directly:
npm run docs:publishBefore running a real release, make sure RELEASE_IT_GITHUB_TOKEN is set and npm whoami --registry https://registry.npmjs.org/ passes. Release-it prompts for an npm OTP when npm requires one.
Contributing
- Fork the repository.
- Create a feature branch:
git checkout -b feature/amazing-feature. - Make your changes.
- Run
npm run buildandnpm test. - Commit, push, and open a pull request.
Licence
ISC. See LICENSE.md.