Before publishing a video or podcast, I care less about whether the waveform looks “big” than whether the exported file behaves on another platform. Loudness, peak headroom, clipping, and DC offset answer different questions. The Creator Audio Pre-Publish Health Check puts those measurements together so a creator can catch an obvious problem before uploading.
The primary reader is a YouTuber, podcast editor, or musician who wants a fast browser-side preflight, not a replacement for a mastering meter. The useful takeaway is how the measurements are derived and why a green result still needs context. The product is a working example of Web Audio analysis, with explicit thresholds rather than a mysterious single score.
Decode the file locally, then inspect the samples
The upload accepts audio and video MIME types plus FLAC, OGG, and OPUS extensions. runAnalysis reads the selected file as an ArrayBuffer, creates an AudioContext at 48 kHz, and calls decodeAudioData:
const arrayBuffer = await selectedFile.value.arrayBuffer();
const audioCtx = new (window.AudioContext || window.webkitAudioContext)({ sampleRate: 48000 });
let audioBuffer;
try {
audioBuffer = await audioCtx.decodeAudioData(arrayBuffer);
} catch {
await audioCtx.close();
throw new Error("decode");
}
await audioCtx.close();
After decoding, the code collects every channel with getChannelData. It computes the DC offset from channel zero, but checks clipping across all channels:
let dcSum = 0;
const len = channels[0].length;
for (let i = 0; i < len; i++) dcSum += channels[0][i];
const dcOffset = dcSum / len;
let clipCount = 0;
let totalSamples = 0;
for (const ch of channels) {
for (let i = 0; i < ch.length; i++) {
if (Math.abs(ch[i]) >= 0.99999) clipCount++;
totalSamples++;
}
}
const clippingRate = clipCount / totalSamples;
The threshold is close to the normalized 0 dBFS ceiling. It is a sample-level clipping indicator, useful for finding flat-topped digital audio, not proof that an analog stage or a codec will never distort.
The displayed peak is the largest decoded sample
The analyzer searches every channel for the greatest absolute sample and converts that amplitude to decibels:
let maxSample = 0;
for (const ch of channels) {
for (let i = 0; i < ch.length; i++) {
const abs = Math.abs(ch[i]);
if (abs > maxSample) maxSample = abs;
}
}
const truePeak = maxSample > 0 ? 20 * Math.log10(maxSample) : -Infinity;
The UI labels this value “True Peak” and advises staying at or below -1.0 dBTP. That is a practical safety target for lossy encoding, but the implementation is measuring the maximum decoded sample; it does not oversample and reconstruct inter-sample peaks. This distinction matters when comparing it with a professional true-peak meter. A file can pass this browser check and still reveal an inter-sample peak after conversion.
LUFS uses an offline filter pass
For loudness, calcLufs builds an OfflineAudioContext, copies at most two channels into a buffer, and processes them through two Biquad filters:
const numCh = Math.min(channels.length, 2);
const offlineCtx = new OfflineAudioContext(numCh, totalSamples, sampleRate);
const preFilter = offlineCtx.createBiquadFilter();
preFilter.type = "highshelf";
preFilter.frequency.value = 1681.974;
preFilter.gain.value = 3.999843853;
const rlbFilter = offlineCtx.createBiquadFilter();
rlbFilter.type = "highpass";
rlbFilter.frequency.value = 38.13547;
rlbFilter.Q.value = 0.5003270373;
It renders the filtered buffer, averages the squared samples, and returns -0.691 + 10 * Math.log10(meanSquare). The result is an approximation of an ITU-R BS.1770-style K-weighted integrated loudness. The platform comparison then uses configured targets: YouTube and Spotify at -14 LUFS, Apple Music at -16, Podcast at -16 with a wider tolerance, Instagram/TikTok/Facebook at -14, and EBU R128 at -23.
The status logic is intentionally simple. Loudness from -16 through -12 LUFS is marked good, values below -24 are a warning, and the middle region is over-limit. That makes the screen actionable for a preflight, while the platform cards show the different target and tolerance choices.
The result object also keeps duration, channel count, and sample rate alongside the headline metrics. Those fields are not decorative: a surprising channel count can explain a loudness mismatch, and a sample rate tells you what the browser actually decoded. The progress values are UI checkpoints rather than a measurement of remaining CPU time—5 before reading, 20 after the buffer is loaded, 50 after decoding, and 100 when analysis finishes—so a very long file can appear to pause while the offline render is still running.
Honest limits before publishing
Everything runs in the browser, but decoding a long, multichannel file still requires memory for the complete AudioBuffer and an offline render. A browser may reject a codec even when its extension looks supported, and the catch block reports a generic decode failure. The tool does not normalize or repair the file; it only measures the decoded data.
DC offset is calculated from the first channel and flagged when its absolute average reaches 0.01. Clipping is a percentage of samples at or above the threshold, not a duration-weighted perceptual measure. Most importantly, the LUFS filter and the sample peak calculation can differ from a DAW because of browser precision, sample-rate behavior, channel handling, and the lack of true-peak oversampling. I use the report to decide what to inspect next, then cross-check a broadcast or release master in a dedicated meter.
I turned this implementation into a small free tool: Creator Audio Pre-Publish Health Check.
Top comments (0)