Using @remotion/whisper-webgpu in Node.jsv4.0.528
You can use the @remotion/whisper-webgpu package in Node.js to transcribe audio.
The following example uses @mediabunny/server to decode and resample a local audio or video file to 16Khz.
Installation
- npm
- bun
- pnpm
- yarn
This assumes you are currently using v4.0.527 of Remotion.npm i --save-exact @remotion/[email protected] @huggingface/transformers mediabunny @mediabunny/server
Also update
remotion and all `@remotion/*` packages to the same version.Remove all
^ character in front of the version numbers of it as it can lead to a version conflict.This assumes you are currently using v4.0.527 of Remotion.pnpm i @remotion/[email protected] @huggingface/transformers mediabunny @mediabunny/server
Also update
remotion and all `@remotion/*` packages to the same version.Remove all
^ character in front of the version numbers of it as it can lead to a version conflict.This assumes you are currently using v4.0.527 of Remotion.bun i @remotion/[email protected] @huggingface/transformers mediabunny @mediabunny/server
Also update
remotion and all `@remotion/*` packages to the same version.Remove all
^ character in front of the version numbers of it as it can lead to a version conflict.This assumes you are currently using v4.0.527 of Remotion.yarn --exact add @remotion/[email protected] @huggingface/transformers mediabunny @mediabunny/server
Also update
remotion and all `@remotion/*` packages to the same version.Remove all
^ character in front of the version numbers of it as it can lead to a version conflict.Example
transcribe.tsimport {registerMediabunnyServer } from '@mediabunny/server'; import {WHISPER_WEBGPU_SAMPLE_RATE ,downloadWhisperModel ,loadWhisperModel ,toCaptions ,transcribe , } from '@remotion/whisper-webgpu'; import {ALL_FORMATS ,Conversion ,FilePathSource ,Input ,NullTarget ,Output ,WavOutputFormat , } from 'mediabunny'; import {writeFile } from 'node:fs/promises';registerMediabunnyServer (); typeWaveformChunk = {startFrame : number;waveform :Float32Array ; }; constchunks :WaveformChunk [] = []; // In long-running apps, put this code in an async function rather than using // `using` at the top level: top-level resources live until the module finishes. usinginput = newInput ({formats :ALL_FORMATS ,source : newFilePathSource ('audio.mp3'), }); constaudioTrack = awaitinput .getPrimaryAudioTrack (); if (audioTrack === null) { throw newError ('The media does not contain an audio track.'); } constconversion = awaitConversion .init ({input ,output : newOutput ({format : newWavOutputFormat (),target : newNullTarget (), }),video : {discard : true},audio : (track ) => { if (track .id !==audioTrack .id ) { return {discard : true}; } return {codec : 'pcm-f32',forceTranscode : true,numberOfChannels : 1,sampleFormat : 'f32',sampleRate :WHISPER_WEBGPU_SAMPLE_RATE ,process : (sample ) => { constwaveform = newFloat32Array (sample .allocationSize ({format : 'f32',planeIndex : 0}) /Float32Array .BYTES_PER_ELEMENT , );sample .copyTo (waveform , {format : 'f32',planeIndex : 0});chunks .push ({startFrame :Math .round (sample .timestamp *WHISPER_WEBGPU_SAMPLE_RATE , ),waveform , }); returnsample ; }, }; }, }); if (!conversion .isValid ) { throw newError ('The audio track cannot be decoded.'); } awaitconversion .execute (); constwaveformLength =chunks .reduce ( (max ,chunk ) =>Math .max (max ,chunk .startFrame +chunk .waveform .length ), 0, ); constchannelWaveform = newFloat32Array (waveformLength ); for (constchunk ofchunks ) { constdestinationStart =Math .max (0,chunk .startFrame ); constsourceStart =Math .max (0, -chunk .startFrame ); constavailableLength =Math .min (chunk .waveform .length -sourceStart ,channelWaveform .length -destinationStart , ); if (availableLength > 0) {channelWaveform .set (chunk .waveform .subarray (sourceStart ,sourceStart +availableLength ),destinationStart , ); } } constmodel = 'small.en'; awaitdownloadWhisperModel ({model }); await usingmodelHandle = awaitloadWhisperModel ({model }); consttranscription = awaittranscribe ({channelWaveform ,model }); const {captions } =toCaptions ({whisperWebGpuOutput :transcription }); awaitwriteFile ('captions.json',JSON .stringify (captions , null, 2));
Call canUseWhisperWebGpu() before loading the model if the script may run on different hardware. It creates and disposes a tiny WebGPU-backed ONNX session, so it verifies that ONNX Runtime can acquire an adapter without downloading a Whisper model.
Implementation notes
A compatible GPU is required for this to work.
Node.js does not have WebGPU APIs available by default.
Transformers.js uses the onnxruntime-node addon instead, whose experimental WebGPU execution provider uses Dawn.
ONNX Runtime does not work on Linux arm64.