tools / extract audio

Extract the audio from a video FREE — NO KEY

Drop in an .mp4, .mov or .webm and take the sound out of it. It happens in this tab: the video is never uploaded anywhere, and there is nothing to pay because nothing leaves your machine. Then send it straight to the app to be transcribed.

Drop a video here or click to choose one — mp4, mov, webm, mkv, avi…

What actually happens

Four steps, and not one of them is an upload.

  1. Your video stays in this tab. Choosing a file does not send it anywhere — there is no server here to send it to.
  2. FFmpeg arrives as WebAssembly, on demand — about 32 MB, the first time you press the button, then cached by your browser. A visitor who only reads this page downloads none of it. It is fetched from unpkg.com via SGraph's sg-video.js.
  3. The audio track is lifted out, not re-made: -vn -c:a copy drops the picture and copies the sound across untouched, so it is fast and loses nothing. The result is an .m4a.
  4. Then it is yours — play it, download it, or hand it to the app for transcription without it ever touching a disk.

When it has to re-encode. Copying only works if the audio can live in an MP4 container. A phone or WhatsApp video is AAC and copies perfectly; a screen recording or a web download is usually Opus in WebM, which cannot be copied into .m4a. Rather than tell you the file has no sound — which is what the underlying error says, and it is wrong — this tool notices, re-encodes to AAC, and says so on the result.

How this works, in full → the whole sequence from pressing the button to the .m4a, why WebAssembly is what makes a tool with no server possible, what it costs, and the two failures this design produced.

For agents: the JS API

The page publishes window.__tool — the same SgToolApi contract the app and the text-to-speech tool use.

checking…

Actionparamsreturns
extractAudio{ file, withAudio?, onProgress? } { filename, mime, bytes, sourceName, reencoded, tookMs } — plus base64 when withAudio is set
prepare{}loads the 32 MB core up front, so a later call is instant
isSupported{}{ supported } — WebAssembly available
probe{ file }duration and dimensions, without loading FFmpeg
getLastAudio{}the last result's metadata
saveLastAudio{ filename? }a real download Playwright can capture
sendToApp{}hands the audio to /app/ for transcription

Audio is opt-in here. A film's soundtrack can be tens of megabytes and base64 makes it a third bigger again, so extractAudio returns metadata by default. Ask for withAudio: true when you want the bytes, or use saveLastAudio and catch the download.

// Playwright: a video on disk, its audio out, no UI involved.
await page.goto('https://voicedebrief.ai/tools/extract-audio/')
await page.waitForFunction(() => window.__tool)

await page.setInputFiles('#file', 'holiday.mp4')
const r = await page.evaluate(() => window.__tool.extractAudio({
  file: document.getElementById('file').files[0], withAudio: true }))

console.log(r.filename, r.bytes, r.reencoded)   // reencoded === true means it was not AAC
await writeFile('holiday.m4a', Buffer.from(r.base64, 'base64'))

Machine-readable: manifest.json · SKILL__api.md · the page emits extract:done. First call pays the 32 MB; call prepare first if you would rather wait once.