{"id":18143547,"url":"https://github.com/domchristie/needles","last_synced_at":"2026-02-26T12:02:08.593Z","repository":{"id":42895179,"uuid":"253631348","full_name":"domchristie/needles","owner":"domchristie","description":"📍Audio loudness metering in the browser","archived":false,"fork":false,"pushed_at":"2023-02-16T18:18:00.000Z","size":917,"stargazers_count":65,"open_issues_count":7,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-09-11T14:55:40.051Z","etag":null,"topics":["audio","audio-analysis","ebur128","lkfs","loudness","loudness-meter-visualisation","loudness-war","lufs","meter","metering"],"latest_commit_sha":null,"homepage":"https://domchristie.github.io/needles/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/domchristie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-04-06T22:36:09.000Z","updated_at":"2025-08-20T21:24:46.000Z","dependencies_parsed_at":"2025-04-23T00:48:49.897Z","dependency_job_id":"7918823a-a3d0-4718-b3c3-8e3cb1082301","html_url":"https://github.com/domchristie/needles","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/domchristie/needles","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domchristie%2Fneedles","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domchristie%2Fneedles/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domchristie%2Fneedles/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domchristie%2Fneedles/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/domchristie","download_url":"https://codeload.github.com/domchristie/needles/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/domchristie%2Fneedles/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29858461,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-26T08:51:08.701Z","status":"ssl_error","status_checked_at":"2026-02-26T08:50:19.607Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","audio-analysis","ebur128","lkfs","loudness","loudness-meter-visualisation","loudness-war","lufs","meter","metering"],"created_at":"2024-11-01T19:07:59.611Z","updated_at":"2026-02-26T12:02:08.577Z","avatar_url":"https://github.com/domchristie.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Needles\nAudio loudness metering in the browser.\n\nFeatures:\n- Live momentary, short-term, and integrated K-weighted loudness (LUFS/LKFS) for a given web audio source.\n- Offline integrated K-weighted loudness for file analysis.\n- Offline momentary and short-term loudness values for higher resolution file analysis.\n- No GUI, it just reports values. The choice of interface and visual style is up to you!\n- Aims to be EBU Mode compliant, following EBU R 128 / ITU-R BS.1770-4.\n- Supports recent versions of Chrome and Firefox.\n\nFuture features:\n- True-peak, dBTP (ITU-R BS.1770-4)\n- Loudness range, LRA (EBU R 128)\n- RMS?\n- Progress events for offline measurements\n- Update `source` and `modes` dynamically\n- Support for Safari (where some features of the Web Audio API are not supported)\n\n## Installation\n\nInstall via npm:\n```\nnpm install @domchristie/needles\n```\nThen make the included `dist/needles-worker.js` file accessible on your server.\n\n## Usage\n\nImport the library and create your source, for example via an `\u003caudio\u003e` element:\n\n```js\nimport { LoudnessMeter } from '@domchristie/needles'\n\nvar AudioContext = window.AudioContext || window.webkitAudioContext\nvar audioContext = new AudioContext()\n\nvar audioElement = document.querySelector('audio')\nvar source = audioContext.createMediaElementSource(audioElement)\n\n// Listen to the output (optional)\nsource.connect(audioContext.destination)\n```\n\nCreate your `loudnessMeter` passing in the source, and the worker path:\n\n```js\nvar loudnessMeter = new LoudnessMeter({\n  source: source,\n  workerUri: 'public/path/to/needles-worker.js'\n})\n```\n\nListen for the `dataavailable` event which reports the loudness for each mode:\n\n```js\nloudnessMeter.on('dataavailable', function (event) {\n  event.data.mode // momentary | short-term | integrated\n  event.data.value // -14\n})\n```\n\nStart metering:\n\n```js\nloudnessMeter.start()\n```\n\n### Offline Analysis\n\nImport the library and create an `AudioContext`:\n\n```js\nimport { LoudnessMeter } from '@domchristie/needles'\n\nvar AudioContext = window.AudioContext || window.webkitAudioContext\nvar audioContext = new AudioContext()\n```\n\nCreate your source: usually by decoding an array buffer from an XHR response, or from reading the file with the `FileReader` API. Once a file has been decoded, create an `OfflineAudioContext` using the buffer properties, followed by a buffer source. Finally, create your `loudnessMeter` passing in the `source`, then `start` the analysis. Offline integrated analyses report `dataavailable` just once, when the entire buffer has been processed.\n\n```js\nvar fileReader = new FileReader()\nfileReader.onload = function (event) {\n  audioContext.decodeAudioData(event.target.result, audioDecoded)\n}\n\nvar fileInput = document.querySelector('input[type=\"file\"]')\nfileInput.onchange = function (event) {\n  if (fileInput.files[0]) {\n    fileReader.readAsArrayBuffer(fileInput.files[0])\n  }\n}\n\nvar OfflineAudioContext = window.OfflineAudioContext || window.webkitOfflineAudioContext\n\nfunction audioDecoded (buffer) {\n  var offlineAudioContext = new OfflineAudioContext(\n    buffer.numberOfChannels,\n    buffer.length,\n    buffer.sampleRate\n  )\n  var source = offlineAudioContext.createBufferSource()\n  source.buffer = buffer\n\n  var loudnessMeter = new Needles({\n    source: source,\n    modes: ['integrated'],\n    workerUri: 'public/path/to/needles-worker.js'\n  })\n\n  loudnessMeter.on('dataavailable', function (event) {\n    console.log(event.data.value)\n  })\n\n  loudnessMeter.start()\n}\n```\n\n## Options\n`Needles` is initialized with an object, requiring `source` property (an audio source node), and a `workerUri` property, referencing the public path of the `needles-worker.js` file.\n\nOperating modes can be chosen by passing in a `modes` array. Possible modes are:\n\n- `momentary`\n- `short-term`\n- `integrated`\n\nFor example, to only meter short-term and integrated readings:\n\n```js\nvar loudnessMeter = new LoudnessMeter({\n  source: source,\n  modes: ['short-term', 'integrated'],\n  workerUri: 'public/path/to/needles-worker.js'\n})\n```\n\n## Methods\n\n### `start`\nStarts processing samples from the `source`. For offline sources, the source and context rendering will also be started.\n\n### `pause`\nPauses metering.\n\n### `resume`\nResumes metering (following a pause).\n\n### `reset`\nResets all meters, reporting any remaining valid measurements in a `dataavailable` event. Event handlers are still maintained.\n\n### `stop`\nStops processing, resets all meters and reports any remaining valid measurements in a `dataavailable` event. Event handlers are still maintained.\n\n### `on(type, handler)`\nListens for an event `type` then calls the `handler`.\n\n### `off(type, handler)`\nRemoves the event handler for the given type. If no handler is specified, all handlers for the given event type are removed. If no arguments are supplied, all event handlers are removed.\n\n## Events\n\n### `dataavailable`\nReports measurements for a given mode. The event's `data` includes:\n- **`mode`**: the operating mode, either `momentary`, `short-term`, or `integrated`\n- **`value`**: the loudness in LUFS\n\n### Other events\n`start`, `pause`, `resume`, and `stop` are triggered after the corresponding methods are called.\n\n## Notes on Implementation\n**Needles** uses the `ScriptProcessorNode` interface to access raw sample data. This is a deprecated API which will be replaced by the Audio Worklet interface (currently only supported in Chrome). **Needles** aims to be prepared for broader Audio Worklet support by implementing adapters for both interfaces. Initial tests using Audio Worklets in Chrome results in glitchy performance, and so are currently disabled. On the plus side, [it doesn't look like `ScriptProcessorNode`s are going away](https://youtu.be/g1L4O1smMC0?t=928) (at least any time soon).\n\n## License\nNeedles is copyright © 2020+ Dom Christie and released under the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdomchristie%2Fneedles","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdomchristie%2Fneedles","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdomchristie%2Fneedles/lists"}