jukette

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 jukette

Then 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-soundcloud

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:

The browser-ready auto-registration file is dist/jukette-auto.min.js.

Distribution files:

Back to top


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'

Back to top


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:

Back to top


Tracks

<jukette-track> attributes and track object fields:

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.

Back to top


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:

Each event includes event.detail with the current track, tracks, index, type, currentTime, duration, and playing.

Back to top


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:

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:

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;
}

Back to top


Roadmap

Forward-looking work lives in ROADMAP.md. For now, that covers the planned SoundCloud addon direction without expanding jukette core again.

Back to top


Development

npm install
npm run dev
npm run build

This repository is an npm workspaces monorepo:

Root scripts orchestrate builds in dependency order. Use npm run build, npm run typecheck, and npm test from the repo root.

Common root shortcuts:

Back to top


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 release

If docs publishing fails after the package release, rerun it directly:

npm run docs:publish

Before 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.

Back to top


Contributing

  1. Fork the repository.
  2. Create a feature branch: git checkout -b feature/amazing-feature.
  3. Make your changes.
  4. Run npm run build and npm test.
  5. Commit, push, and open a pull request.

Licence

ISC. See LICENSE.md.