Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/kepabilbao67-bot/musicplayer2/llms.txt

Use this file to discover all available pages before exploring further.

NEON DJ’s entire sound engine is built on the Web Audio API — there are no third-party audio libraries loaded at startup. Every oscillator, filter, delay, convolution reverb, and limiter is a native browser node. This document catalogues every node type in use, where it appears, and what parameters are configured.
Browsers enforce an autoplay policy: an AudioContext created before a user gesture starts in the "suspended" state and produces no audio. NEON DJ defers AudioContext creation to the first interactive event and also calls AC().resume() at the start of every handler that could trigger sound. If you integrate NEON DJ’s patterns into another project, always ensure user interaction has occurred before calling audio-producing code.

Node Reference Table

Node TypeWhere UsedKey Parameters
AudioContextGlobal singleton via AC()sampleRate (device default, typically 44100 Hz)
OfflineAudioContextgenerateTrack() — pre-renders all genre tracks2 channels, 44100 Hz, length = bars × 16 × stepDur × sampleRate
AudioBufferSourceNodeDeck playback, grain playback (scratch), all synth functionsplaybackRate, loop, loopStart, loopEnd
BiquadFilterNodeEQ (3 per deck), per-deck filter, snare HP, hat HP, bass LP, lead (none), chord LP, wobble LP, FX noise BPVarious — see detail below
GainNodeDeck volume, crossfader, delay send, reverb send, master gain, flanger wet, echo wet, all synth envelopesgain AudioParam
DynamicsCompressorNodeMaster limiterthreshold −3 dB, knee 0, ratio 20, attack 3 ms, release 250 ms
ConvolverNodePer-deck reverbSynthesised 1.8 s impulse response (exponentially decaying noise)
DelayNodePer-deck echo (up to 2.0 s max), master echo (1.0 s max), master flanger (0.05 s max)delayTime AudioParam
OscillatorNodeAll synth functions (kick, bass, lead, chord, wobble, FX riser, airhorn, impact, zap), flanger LFOtype, frequency AudioParam
AnalyserNodePer-deck VU meter (time-domain), master spectrum display (frequency-domain)fftSize 256 (deck), 512 (master); smoothingTimeConstant 0.8 (master)
WaveShaperNodesynthChord() — guitar power-chord distortioncurve (custom soft-clip), oversample "2x"
ScriptProcessorNodeRecording — captures PCM from master outputBuffer size 4096, 2 input channels, 2 output channels

Node Details

AudioContext / webkitAudioContext

let audioCtx = null;
function AC() {
  if (!audioCtx)
    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
  return audioCtx;
}
The AC() helper is the single access point to the audio context. All other code calls AC() rather than accessing audioCtx directly, ensuring the context is only created once and only after a user gesture.

OfflineAudioContext

const ctx = new (window.OfflineAudioContext || window.webkitOfflineAudioContext)(
  2,       // channels
  length,  // total samples = bars × 16 steps × stepDuration × 44100
  44100    // sample rate
);
OfflineAudioContext renders audio faster than real-time without sending it to the speakers. NEON DJ uses it inside generateTrack() to pre-render all genre tracks during the loading screen. The result is a standard AudioBuffer stored in DEMO_BUFFERS[key].

AudioBufferSourceNode

A new AudioBufferSourceNode is created by startSource() every time a deck starts playing, because source nodes can only be started once:
startSource() {
  const ctx = AC();
  this.source = ctx.createBufferSource();
  this.source.buffer = this.buffer;
  this.source.playbackRate.value = this.rate;
  if (this.buffer._loop && !this.loopActive) { this.source.loop = true; }
  // ...
  this.source.connect(this.eqLow);
  this.source.start(0, this.offset);
}
The same pattern is used for grain playback during vinyl scratching (playGrain()) and for every note scheduled inside generateTrack().

BiquadFilterNode

Used in many different roles across the codebase:
Three filters chained in series, constructed in buildGraph():
this.eqLow  = ctx.createBiquadFilter();
this.eqLow.type = "lowshelf";
this.eqLow.frequency.value = 200;     // dB gain controlled by knob

this.eqMid  = ctx.createBiquadFilter();
this.eqMid.type = "peaking";
this.eqMid.frequency.value = 1000;
this.eqMid.Q.value = 0.9;

this.eqHigh = ctx.createBiquadFilter();
this.eqHigh.type = "highshelf";
this.eqHigh.frequency.value = 3800;

DynamicsCompressorNode

The master limiter sits between masterGain and ctx.destination:
masterLimiter = ctx.createDynamicsCompressor();
masterLimiter.threshold.value = -3;
masterLimiter.knee.value      =  0;
masterLimiter.ratio.value     = 20;
masterLimiter.attack.value    = 0.003;
masterLimiter.release.value   = 0.25;
A ratio of 20:1 with zero knee behaves as a near-brickwall limiter. The fast 3 ms attack catches transient peaks before they clip.

ConvolverNode

Each deck has its own ConvolverNode for reverb. The impulse response is synthesised once and shared via getReverbIR():
function getReverbIR(ctx) {
  const dur = 1.8,  len = Math.floor(ctx.sampleRate * dur);
  const ir = ctx.createBuffer(2, len, ctx.sampleRate);
  for (let ch = 0; ch < 2; ch++) {
    const d = ir.getChannelData(ch);
    for (let i = 0; i < len; i++)
      d[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / len, 2.5);
  }
  _reverbIR = ir;
  return ir;
}
The exponential decay (Math.pow(1 - i/len, 2.5)) gives a smooth room tail without the harsh flutter of linear decay.

AnalyserNode

Two separate analysers are in use:
  • Per-deck: fftSize = 256, reads time-domain data (getByteTimeDomainData) for the 14-segment VU meter display.
  • Master spectrum: fftSize = 512, smoothingTimeConstant = 0.8, reads frequency-domain data (getByteFrequencyData) for the spectrum canvas at the top of the page. Connected after masterLimiter via finalNode().

WaveShaperNode

Used only in synthChord() to add guitar-style power-chord distortion:
function makeDistCurve(k) {
  const n = 1024, curve = new Float32Array(n);
  for (let i = 0; i < n; i++) {
    const x = i * 2 / n - 1;
    curve[i] = (3 + k) * x * 0.4 / (1 + k * Math.abs(x));
  }
  return curve;
}
// oversample "2x" reduces aliasing from the nonlinearity
shaper.oversample = "2x";

ScriptProcessorNode

The recording system uses ScriptProcessorNode (the legacy PCM capture API) to collect raw stereo float samples from the master output:
recProc = AC().createScriptProcessor(4096, 2, 2);
recProc.onaudioprocess = ev => {
  if (!recording) return;
  recL.push(new Float32Array(ev.inputBuffer.getChannelData(0)));
  recR.push(new Float32Array(ev.inputBuffer.getChannelData(1)));
};
The collected chunks are assembled and encoded to WAV by encodeWAV(), or to MP3 by encodeMP3() if the lamejs library can be loaded dynamically from a CDN. ScriptProcessorNode is deprecated in favour of AudioWorkletNode, but remains broadly supported and avoids the complexity of loading a worklet module in a single-file context.
For deeper reference on every node type used here, see the MDN Web Audio API documentation. The Web Audio API specification covers the full parameter ranges and processing models.

Build docs developers (and LLMs) love