Skip to content

Crossfade Methods

crossfadeTo(track, opts?)

TypeScript
crossfadeTo(track: T, opts?: CrossfadeOptions & ActionOptions): Promise<void>

Start a crossfade to track. Resolves when the fade completes and the new track is the primary.

Short-circuits:

  • isTransitioning() === true, no-op (stacked crossfades are rejected)
  • opts.duration === 0, instant swap, no gain ramp
  • track.url is absent, throws MediaFormatError
TypeScript
await player.crossfadeTo(nextTrack, {
duration: 5, // seconds
curve: 'equal-power',
startAt: 0, // start position in the incoming track, ms
});

isTransitioning()

TypeScript
isTransitioning(): boolean

Returns true while a crossfade is in progress. Check before calling crossfadeTo() when the caller needs to guard against re-entrancy.

TypeScript
if (!player.isTransitioning()) {
await player.crossfadeTo(nextTrack, { duration: 5 });
}

CrossfadeOptions

TypeScript
interface CrossfadeOptions {
duration: number;
curve?: 'linear' | 'equal-power';
startAt?: number;
}

duration

Crossfade length in seconds. When omitted per-call, falls back to crossfadeDefaults.duration from setup(). If neither is set, crossfadeTo() defaults to 5 seconds.

curve

Gain ramp shape:

  • 'equal-power': cos / sin ramps. Perceived loudness stays constant throughout. Recommended for music.
  • 'linear': straight ramp. Creates a ~3 dB dip at the midpoint.

Default: falls back to crossfadeDefaults.curve from setup(), or 'equal-power' if neither is configured. The curve shapes the auto-advance transition; a manual crossfadeTo() call ramps by duration only.

startAt

Start position in milliseconds of the incoming track (default 0). Use to begin the fade mid-track rather than from the beginning.

Events

crossfadeStart

TypeScript
player.on('crossfadeStart', ({ from, to, duration }) => {
// from: BasePlaylistItem | null (null when no current track)
// to: BasePlaylistItem
// duration: number (milliseconds, converted from the seconds in CrossfadeOptions)
showFadeUI(to.name, duration);
});

duration in this payload is in milliseconds, not seconds.

crossfadeComplete

TypeScript
player.on('crossfadeComplete', ({ track }) => {
hideFadeUI();
console.log('Now primary:', track.name);
});

Execution sequence

StepActionNotes
1guardsisTransitioning true → no-op; url missing → MediaFormatError
2_isTransitioning = truemarks transition in progress
3emit crossfadeStartfires before audio nodes are touched
4backend.loadSecondary(url)loads incoming track into secondary audio node
5backend.primeSecondary(opts.startAt)seeks secondary to the requested start position
6backend.crossfade(durationMs)runs the gain ramp on both nodes
7item(track.id)updates queue cursor, emits current
8_isTransitioning = falseclears transition flag
9emit crossfadeCompletesignals callers that the new track is primary

Full example

TypeScript
import nmMPlayer from '@nomercy-entertainment/nomercy-music-player';
import { AutoAdvancePlugin } from '@nomercy-entertainment/nomercy-music-player/plugins';

const player = nmMPlayer('main')
.addPlugin(AutoAdvancePlugin, {
crossfade: true,
crossfadeDuration: 5,
})
.setup({
baseUrl: 'https://raw.githubusercontent.com/NoMercy-Entertainment/nomercy-media/master/Music',
crossfadeDefaults: {
duration: 5,
curve: 'equal-power',
},
trackEndingSoonThreshold: 8,
playlist: [
{
id: 'kjc-01',
name: 'Thaw You Out',
url: '/D/Derek%20Clegg/%5B2010%5D%20KJC/01%20Thaw%20You%20Out.mp3',
artist: 'Derek Clegg',
},
{
id: 'kjc-02',
name: 'Hope',
url: '/D/Derek%20Clegg/%5B2010%5D%20KJC/02%20Hope.mp3',
artist: 'Derek Clegg',
},
],
});

player.on('crossfadeStart', ({ from, to, duration }) => {
console.log(`Fading from "${from?.name}" to "${to.name}" over ${duration}ms`);
});

player.on('crossfadeComplete', ({ track }) => {
console.log('Now playing:', track.name);
});

player.on('ready', () => {
player.item(0, { autoplay: true });
});

See also