Skip to content

lcweden/loudness-worklet

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

314 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Loudness Worklet

npm version license demo

A loudness meter for the Web Audio API, based on the ITU-R BS.1770-5 standard and implemented as an AudioWorkletProcessor.

screenshot

Features

  • Standard Compliant: Strictly follows ITU-R BS.1770-5 for accurate loudness measurement.
  • Comprehensive Metrics: Calculates Momentary, Short-term, and Integrated Loudness, plus Loudness Range (LRA) and True-Peak levels.
  • Versatile Input: Seamlessly supports both live audio streams ("Microphone/WebRTC") and offline file analysis.
  • Zero Dependencies: Lightweight, pure AudioWorklet implementation requiring no external libraries.

Installation

NPM

Install via npm:

npm install loudness-worklet

Use helper functions to create and load the worklet:

import { createLoudnessWorklet } from "loudness-worklet";

const worklet = await createLoudnessWorklet(audioContext);

or

import { LoudnessWorkletNode } from "loudness-worklet";

await LoudnessWorkletNode.loadModule(audioContext);
const worklet = new LoudnessWorkletNode(audioContext);

CDN

Import directly in your browser using jsDelivr or unpkg:

import { createLoudnessWorklet, LoudnessWorkletNode } from "https://cdn.jsdelivr.net/npm/loudness-worklet/+esm";

Download

For strict Content Security Policy (CSP) environments restricting blob: or eval() via script-src and worker-src, or for offline integration, download the pre-built loudness.worklet.js payload.

  1. Download the pre-built file from GitHub Releases: loudness.worklet.js.
  2. Place loudness.worklet.js in your project directory (e.g., /public/).
await audioContext.audioWorklet.addModule("loudness.worklet.js");
const worklet = new AudioWorkletNode(audioContext, "loudness-processor");

Quick Start

Example

This example shows the easiest way to get started with the loudness-worklet by measuring the loudness of a screen share stream with audio (e.g., a YouTube tab).

<!DOCTYPE html>
<html>
  <body>
    <button>Share Screen</button>
    <pre></pre>
    <script type="module">
      import { createLoudnessWorklet } from "https://cdn.jsdelivr.net/npm/loudness-worklet/+esm";

      const button = document.querySelector("button");
      const pre = document.querySelector("pre");

      button.onclick = async () => {
        // Get the display media stream with audio
        const mediaStream = await navigator.mediaDevices.getDisplayMedia({ audio: true });
        const context = new AudioContext();

        // Create the loudness worklet node automatically
        const worklet = await createLoudnessWorklet(context, {
          processorOptions: {
            interval: 0.02, // every 0.02s a message will be sent
            capacity: 600 // 1 minute of history can be stored
          }
        });

        // Create the audio node from the stream
        const source = new MediaStreamAudioSourceNode(context, { mediaStream });

        worklet.port.onmessage = (event) => {
          pre.textContent = JSON.stringify(event.data, null, 2);
        };

        // Connect the nodes
        source.connect(worklet);
      };
    </script>
  </body>
</html>

File-based measurement

You can measure the loudness of audio files using OfflineAudioContext.

import { LoudnessWorkletNode } from "loudness-worklet";

const input = document.querySelector("input");

input.addEventListener("change", async (event) => {
  const file = event.target.files[0];
  const arrayBuffer = await file.arrayBuffer();
  const audioBuffer = await new AudioContext().decodeAudioData(arrayBuffer);
  const { numberOfChannels, length, sampleRate } = audioBuffer;
  const context = new OfflineAudioContext(numberOfChannels, length, sampleRate);

  await LoudnessWorkletNode.loadModule(context);

  const source = new AudioBufferSourceNode(context, { buffer: audioBuffer });
  const worklet = new LoudnessWorkletNode(context);

  worklet.port.onmessage = (event) => console.log("Loudness Data:", event.data);

  source.connect(worklet).connect(context.destination);
  source.start();

  await context.startRendering();
});

Live-based measurement

Supports all kinds of audio input.

import { createLoudnessWorklet } from "loudness-worklet";

const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
const context = new AudioContext({ sampleRate: 48000 });
const source = context.createMediaStreamSource(mediaStream);
const worklet = await createLoudnessWorklet(context, {
  processorOptions: { interval: 1, capacity: 600 }
});

worklet.port.onmessage = (event) => console.log("Loudness Data:", event.data);
source.connect(worklet);

// worklet.connect(context.destination);
// Optionally connect to destination for monitoring (echo)

Concepts

Contexts

Provide the execution environment for audio processing.

AudioContext

AudioContext is used for real-time audio processing, such as live audio input from a microphone or media stream.

OfflineAudioContext

OfflineAudioContext is used for processing audio data offline, allowing for rendering and analysis without requiring real-time playback.

Nodes

Nodes are the building blocks of an audio graph, representing audio sources, processing modules, and destinations. The following nodes are commonly used as a source input:

AudioBufferSourceNode

AudioBufferSourceNode is used to play audio data stored in an AudioBuffer, typically for pre-recorded audio files.

const arrayBuffer = await file.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
const bufferSource = new AudioBufferSourceNode(audioContext, { buffer: audioBuffer });

MediaStreamAudioSourceNode

MediaStreamAudioSourceNode is used to play audio from a MediaStream, such as a live microphone input or a video element.

const mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
const mediaStreamSource = new MediaStreamAudioSourceNode(audioContext, { mediaStream });

MediaElementAudioSourceNode

MediaElementAudioSourceNode is used to play audio from an HTML <audio> or <video> element.

const mediaElement = document.querySelector("audio");
const elementSource = new MediaElementAudioSourceNode(audioContext, { mediaElement });

API

Options

The AudioWorkletNode constructor accepts the following options:

Params

Option Type Required Default Description
processorOptions.interval number No 0.02 Message interval in seconds.
processorOptions.capacity number No 0 Maximum seconds of history to keep. If set to 0, the processor will not limit the history size.

Example

Most of the time, you only need to set processorOptions or leave it empty.

const { numberOfChannels, length, sampleRate } = audioBuffer;
const worklet = new AudioWorkletNode(context, "loudness-processor", {
  processorOptions: {
    capacity: length / sampleRate,
    interval: 0.02
  }
});

Message Format

Measurement results are sent back to the main thread via port.onmessage as a LoudnessSnapshot object:

type LoudnessMeasurements = {
  momentaryLoudness: number;
  shortTermLoudness: number;
  integratedLoudness: number;
  maximumMomentaryLoudness: number;
  maximumShortTermLoudness: number;
  maximumTruePeakLevel: number;
  loudnessRange: number;
};

type LoudnessSnapshot = {
  currentFrame: number;
  currentTime: number;
  currentMeasurements: LoudnessMeasurements[];
};

Details

Units

Metric Unit
momentaryLoudness LUFS/LKFS
shortTermLoudness LUFS/LKFS
integratedLoudness LUFS/LKFS
maximumMomentaryLoudness LUFS/LKFS
maximumShortTermLoudness LUFS/LKFS
maximumTruePeakLevel dBTP
loudnessRange LU

Supported Channels

Supported channel counts: 1, 2, 5, 6, 8, 10, 12, 24

Note

Channel counts not listed above are weighted at 1.0.

Coefficients

The following coefficients are used for the K-weighting filter:

highshelf highpass
a1 -1.69065929318241 -1.99004745483398
a2 0.73248077421585 0.99007225036621
b0 1.53512485958697 1.0
b1 -2.69169618940638 -2.0
b2 1.19839281085285 1.0

The following FIR filter coefficients are used for true-peak measurement:

Phase 0 Phase 1 Phase 2 Phase 3
0.0017089843750 -0.0291748046875 -0.0189208984375 -0.0083007812500
0.0109863281250 0.0292968750000 0.0330810546875 0.0148925781250
-0.0196533203125 -0.0517578125000 -0.0582275390625 -0.0266113281250
0.0332031250000 0.0891113281250 0.1015625000000 0.0476074218750
-0.0594482421875 -0.1665039062500 -0.2003173828125 -0.1022949218750
0.1373291015625 0.4650878906250 0.7797851562500 0.9721679687500
0.9721679687500 0.7797851562500 0.4650878906250 0.1373291015625
-0.1022949218750 -0.2003173828125 -0.1665039062500 -0.0594482421875
0.0476074218750 0.1015625000000 0.0891113281250 0.0332031250000
-0.0266113281250 -0.0582275390625 -0.0517578125000 -0.0196533203125
0.0148925781250 0.0330810546875 0.0292968750000 0.0109863281250
-0.0083007812500 -0.0189208984375 -0.0291748046875 0.0017089843750

Validation

ITU-R BS.2217

Code correctness is verified against the official ITU-R BS.2217 compliance test suite, ensuring strict adherence to the ITU-R BS.1770 specification.

file measurement channels
1770Comp_2_RelGateTest -10.0 LKFS 2
1770Comp_2_AbsGateTest -69.5 LKFS 2
1770Comp_2_24LKFS_25Hz_2ch -24.0 LKFS 2
1770Comp_2_24LKFS_100Hz_2ch -24.0 LKFS 2
1770Comp_2_24LKFS_500Hz_2ch -24.0 LKFS 2
1770Comp_2_24LKFS_1000Hz_2ch -24.0 LKFS 2
1770Comp_2_24LKFS_2000Hz_2ch -24.0 LKFS 2
1770Comp_2_24LKFS_10000Hz_2ch -24.0 LKFS 2
1770Comp_2_23LKFS_25Hz_2ch -23.0 LKFS 2
1770Comp_2_23LKFS_100Hz_2ch -23.0 LKFS 2
1770Comp_2_23LKFS_500Hz_2ch -23.0 LKFS 2
1770Comp_2_23LKFS_1000Hz_2ch -23.0 LKFS 2
1770Comp_2_23LKFS_2000Hz_2ch -23.0 LKFS 2
1770Comp_2_23LKFS_10000Hz_2ch -23.0 LKFS 2
1770Comp_2_18LKFS_FrequencySweep -18.0 LKFS 1
1770Comp_2_24LKFS_SummingTest -24.0 LKFS 6
1770Comp_2_23LKFS_SummingTest -23.0 LKFS 6
1770Comp_2_24LKFS_ChannelCheckLeft -24.0 LKFS 6
1770Comp_2_24LKFS_ChannelCheckRight -24.0 LKFS 6
1770Comp_2_24LKFS_ChannelCheckCentre -24.0 LKFS 6
1770Comp_2_24LKFS_ChannelCheckLFE -inf LKFS 6
1770Comp_2_24LKFS_ChannelCheckLs -24.0 LKFS 6
1770Comp_2_24LKFS_ChannelCheckRs -24.0 LKFS 6
1770Comp_2_23LKFS_ChannelCheckLeft -23.0 LKFS 6
1770Comp_2_23LKFS_ChannelCheckRight -23.0 LKFS 6
1770Comp_2_23LFKS_ChannelCheckCentre -23.0 LKFS 6
1770Comp_2_23LKFS_ChannelCheckLFE -inf LKFS 6
1770Comp_2_23LKFS_ChannelCheckLs -23.0 LKFS 6
1770Comp_2_23LKFS_ChannelCheckRs -23.0 LKFS 6
1770-2 Conf 6ch VinCntr-24LKFS -24.0 LKFS 6
1770-2 Conf 6ch VinL+R-24LKFS -24.0 LKFS 6
1770-2 Conf 6ch VinL-R-C-24LKFS -24.0 LKFS 6
1770-2 Conf Stereo VinL+R-24LKFS -24.0 LKFS 2
1770-2 Conf Mono Voice+Music-24LKFS -24.0 LKFS 1
1770-2 Conf 6ch VinCntr-23LKFS -23.0 LKFS 6
1770-2 Conf 6ch VinL+R-23LKFS -23.0 LKFS 6
1770-2 Conf 6ch VinL-R-C-23LKFS -23.0 LKFS 6
1770-2 Conf Stereo VinL+R-23LKFS -23.0 LKFS 2
1770-2 Conf Mono Voice+Music-23LKFS -23.0 LKFS 1
1770Conf-8channels_24LKFS -24.0 LKFS 8
1770Conf-8channels_23LKFS -23.0 LKFS 8
1770Conf-10channels_24LKFS -24.0 LKFS 10
1770Conf-10channels_23LKFS -23.0 LKFS 10
1770Conf-12channels_24LKFS -24.0 LKFS 12
1770Conf-12channels_23LKFS -23.0 LKFS 12
1770Conf-24channels_24LKFS -24.0 LKFS 24
1770Conf-24channels_23LKFS -23.0 LKFS 24

Tip

If decodeAudioData fails, it's often due to the browser's specific support for the audio file's format (codec), container, or channel layout, rather than a general API incompatibility. Try a different browser or convert the audio file to a more widely supported format. For example, Chrome has limited support for certain codecs in audio files, while Safari offers broader support. (1770Conf-24channels_24LKFS)

EBU TECH 3341 Minimum requirements test signals

Validated against EBU TECH 3341 minimum requirements for loudness metering, including gating behavior, time scales, and true-peak accuracy.

file expected response and accepted tolerances
seq-3341-1-16bit M, S, I = -23.0 ±0.1 LUFS
seq-3341-2-16bit M, S, I = -33.0 ±0.1 LUFS
seq-3341-3-16bit-v02 I = -23.0 ±0.1 LUFS
seq-3341-4-16bit-v02 I = -23.0 ±0.1 LUFS
seq-3341-5-16bit-v02 I = -23.0 ±0.1 LUFS
seq-3341-6-5channels-16bit I = -23.0 ±0.1 LUFS
seq-3341-6-6channels-WAVEEX-16bit I = -23.0 ±0.1 LUFS
seq-3341-7_seq-3342-5-24bit I = -23.0 ±0.1 LUFS
seq-3341-2011-8_seq-3342-6-24bit-v02 I = -23.0 ±0.1 LUFS
seq-3341-9-24bit S = -23.0 ±0.1 LUFS, constant after 3 s
seq-3341-10-*-24bit Max S = -23.0 ±0.1 LUFS, for each segment
seq-3341-11-24bit Max S = -38.0, -37.0, …, -19.0 ±0.1 LUFS, successive values
seq-3341-12-24bit M = -23.0 ±0.1 LUFS, constant after 1 s
seq-3341-13-*-24bit Max M = -23.0 ±0.1 LUFS, for each segment
seq-3341-14-24bit Max M = -38.0, …, -19.0 ±0.1 LUFS, successive values
seq-3341-15-24bit Max true-peak = -6.0 +0.2/-0.4 dBTP
seq-3341-16-24bit Max true-peak = -6.0 +0.2/-0.4 dBTP
seq-3341-17-24bit Max true-peak = -6.0 +0.2/-0.4 dBTP
seq-3341-18-24bit Max true-peak = -6.0 +0.2/-0.4 dBTP
seq-3341-19-24bit Max true-peak = +3.0 +0.2/-0.4 dBTP
seq-3341-20-24bit Max true-peak = 0.0 +0.2/-0.4 dBTP
seq-3341-21-24bit Max true-peak = 0.0 +0.2/-0.4 dBTP
seq-3341-22-24bit Max true-peak = 0.0 +0.2/-0.4 dBTP
seq-3341-23-24bit Max true-peak = 0.0 +0.2/-0.4 dBTP

EBU TECH 3342 Minimum requirements test signals

EBU TECH 3342 focuses on the measurement of loudness range.

file expected response and accepted tolerances
seq-3342-1-16bit LRA = 10 ±1 LU
seq-3342-2-16bit LRA = 5 ±1 LU
seq-3342-3-16bit LRA = 20 ±1 LU
seq-3342-4-16bit LRA = 15 ±1 LU
seq-3341-7_seq-3342-5-24bit LRA = 5 ±1 LU
seq-3341-2011-8_seq-3342-6-24bit-v02 LRA = 15 ±1 LU

Acknowledgments

This project is a learning experiment aimed at exploring audio signal processing and ITU-R BS.1770 loudness measurement standards. I am not an expert in audio engineering or signal processing, and this project was developed as a way to better understand the concepts of audio loudness and implementation techniques. Thanks to the ITU-R BS.1770 standards for providing the theoretical basis for loudness measurement.

License

This project is licensed under the MIT License.

References

About

Measures audio loudness in the browser with the Web Audio API. A lightweight, ITU-R BS.1770-5 compliant meter, validated with official ITU-R (BS.2217) and EBU (TECH 3341, 3342) test suites. Provides momentary, short-term, integrated loudness (LUFS), LRA, and true-peak analysis.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Contributors

Languages