Pulse

Side A · ready

Web Components reference

@pulse-music/web-component registers three elements as soon as it is imported. Registration is idempotent, so importing the package from several bundles never throws.

A machine-readable Custom Elements Manifest (custom-elements.json) ships with the package for IDEs and doc tools.


<pulse-player>

<pulse-player variant="midnight" accent-color="#a78bfa" ambient-eq resizable></pulse-player>

The card fills the width of its container and adapts to it:

Container width Layout
≥ 460 px Full: artwork, equaliser, title, artist, transport, time, mute
360 – 459 px Mute button folds away
280 – 359 px Time and the actions slot fold away
210 – 279 px Previous / next fold away
128 – 209 px Compact: artwork, title and play
< 128 px Disc: round artwork with a progress ring

Give the element a width in flex/grid layouts (it is a size container, so it doesn’t size itself from its content).

Attributes and properties

Attribute Property Type Default Description
variant variant PulseVariant 'auto' Theme. Reflected.
accent-color accentColor CSS colour — Accent for progress, equaliser, focus rings. auto samples it from the cover when unset.
custom-background customBackground CSS background — Background used by variant="custom".
ambient-eq ambientEq boolean engine’s ambientEq Live equaliser drawn behind the card.
grain grain boolean true Film-grain overlay; grain="false" removes it.
resizable resizable boolean false Corner handle to resize the player: pointer drag, or arrow keys / Home / End when focused; Enter or a double-click restores the automatic width.
resize-min resizeMin number (px) 64 Minimum width while resizing.
resize-max resizeMax number (px) 760 Maximum width while resizing (also bounded by the parent).
session session string 'default' Audio session to join.
— engine PulseEngine — Bind to an explicit engine instead of a named session.
— tracks Track[] — Replace the session’s playlist.
— labels Partial<PulseLabels> — Localised strings.

Slots

Slot Description
actions Extra controls in the top-right corner of wide players — links, share buttons…

Light-DOM <pulse-track> children are read as the playlist and are never rendered.

CSS parts

player, artwork, title, artist, controls, progress, time.

pulse-player::part(title) {
  font-family: 'Fraunces', serif;
  letter-spacing: 0;
}

CSS custom properties

Property Description
--pulse-accent Accent colour (same as accent-color).
--pulse-bg Card background.
--pulse-fg Text and icon colour.
--pulse-muted Secondary text colour.
--pulse-border Hairline border colour.
--pulse-radius Corner radius of the card.
--pulse-font Font family (inherits from the page by default).
--pulse-shadow Outer shadow.
--pulse-ink Icon colour on the main play button.
--pulse-error Colour of the error message.

Custom states

Style the host from the outside with :state():

pulse-player:state(playing) {
  outline: 1px solid var(--brand);
}
pulse-player:state(loading) {
  cursor: progress;
}
pulse-player:state(error) {
  opacity: 0.8;
}

<pulse-fab>

<pulse-fab placement="bottom-end" pulso></pulse-fab>

A floating disc bound to the same session. Tap to play or pause; drag it anywhere and it springs to the nearest screen edge, remembering the spot in localStorage. Long-press, right-click, the ⋯ badge or Shift+F10 open a menu with previous, next and close.

Attribute Property Type Default Description
variant variant PulseVariant 'auto' Theme.
accent-color accentColor CSS colour — Accent colour.
placement placement 'bottom-end' | 'bottom-start' | 'top-end' | 'top-start' | 'inline' 'bottom-end' Screen corner, or inline to sit in the document flow.
reveal reveal 'on-play' | 'always' 'on-play' Show once playback starts, or immediately.
size size number (px) 64 Diameter.
pulso pulso boolean false Heartbeat ripple while playing.
locked locked boolean false Disable dragging.
persist-key persistKey string 'pulse-fab-position' Storage key for the dragged position; "" disables persistence.
session session string 'default' Audio session.
— engine PulseEngine — Explicit engine.
— labels Partial<PulseLabels> — Localised strings.

CSS: --pulse-accent, --pulse-fab-z (stacking order, default 1000). The disc respects env(safe-area-inset-*).

Choosing Close in the menu pauses playback and hides the FAB (with reveal="on-play") until music starts again.


<pulse-track>

Declares a playlist entry in markup. Changes to the children or their attributes update the playlist live.

<pulse-player>
  <pulse-track
    src="/a.mp3"
    title="Intro"
    artist="Me"
    album="Demo"
    cover="/a.jpg"
    cover-pos="50% 20%"
    cover-scale="1.1"
  ></pulse-track>
  <pulse-track src="/b.mp3" title="Outro"></pulse-track>
</pulse-player>
Attribute Maps to
src Track.src (required)
title Track.title (defaults to the file name)
artist Track.artist
album Track.album
cover Track.cover
cover-pos Track.coverPos — CSS object-position
cover-scale Track.coverScale

Sessions

All players with the same session (default: 'default') share one audio element and stay in sync. Give a player its own session for an independent stream — for example a podcast next to background music:

<pulse-player session="music"></pulse-player> <pulse-player session="podcast"></pulse-player>

To configure a session’s engine (options, CORS…), install your own before the players mount:

import { PulseEngine, setSharedEngine } from '@pulse-music/web-component'

setSharedEngine(new PulseEngine({ tracks, crossOrigin: 'anonymous', repeat: 'none' }), 'music')

Events

Both players dispatch these on the element itself. They don’t bubble, so a page with several players never receives duplicates.

Event detail When
pulse-play { track, time } Playback started or resumed.
pulse-pause { track, time } Playback paused (not at a natural track end).
pulse-trackchange { from, to, track } The active track changed.
pulse-ended { track } A track reached its end.
pulse-error { track, reason, detail } reason is 'play-rejected' (autoplay policy) or 'media-error' (load / decode).
pulse-resize { width } <pulse-player> only — the user resized it.

For page-level logic, prefer subscribing to the engine directly — see the engine reference.

Labels

Every visible or spoken string can be replaced:

document.querySelector('pulse-player').labels = {
  play: 'Lecture',
  pause: 'Pause',
  previous: 'Piste précédente',
  next: 'Piste suivante',
  seek: 'Position',
  nowPlaying: 'En cours',
  mute: 'Couper le son',
  unmute: 'Réactiver le son',
  loading: 'Chargement…',
  error: 'Lecture impossible',
  empty: 'Aucune piste',
  resize: 'Redimensionner',
  options: 'Options du lecteur',
  close: 'Fermer le lecteur',
  player: 'Lecteur audio',
  timeText: '{current} sur {duration}',
}

The defaults are exported as DEFAULT_LABELS.

Keyboard

Shortcuts apply while focus is inside a player:

Keys Action
Space, K Play / pause (Space is left to the focused button when on one)
J / L Back / forward 10 s
M Mute / unmute
Shift+N / Shift+P Next / previous track
← → ↑ ↓ on the seek bar ∓ 5 s (with Shift: 1 s)
PageUp / PageDown on the seek bar ± 30 s
Home / End on the seek bar Start / end
Arrows / Home / End / Enter on the resize handle ± 16 px (with Shift: 64 px) / minimum / maximum / automatic width
Shift+F10 or ContextMenu on the FAB Open the menu; arrows move, Esc closes

Variants

auto · transparent · solid · dark · light · sunset · midnight · aurora · vinyl · custom — see Theming.

Pulse — LP · 2026 · MIT

Reading times are approximate. Music: HoliznaCC0, public domain (CC0).