audioproxy
Compare ยท pick the right tool, including when it is not this one

Five ways to handle user audio.

You have audio in your app and need variants: previews, downloads, waveforms, normalized versions. Five honest answers. The right one depends on where you are, and sometimes it is not this one.

audioproxyPre-generate in your appCloudinaryMuxBatch pipelineMediaConvert, Transloadit
Variants on demand, nothing rendered in advanceYes. First request renders and streams; every one after is a cache hitNo. Pre-rendering is the mechanismYes, via transformation URLsClipping of assets; audio is not the focusNo. Jobs run ahead of requests
Waveform data for player UIsYes. audiowaveform-compatible JSON or binary, cached like audioYou run audiowaveform yourself and store the outputWaveform images, not dataNot offered for audioAnother job to write
Loudness normalizationEBU R128, one URL option, per variantYour ffmpeg flags, your jobVolume in percent or dB; no loudness target in the audio transformation referencenormalize_audio at asset creation, -24 LUFS. Fixed target, and not changeable afterwardsITU-R BS.1770-1 to -4 with a target LKFS you choose
Speech cleanupenhance:voice: high-pass, denoise, de-ess, compress, limit behind one option, cached like any variantYour own chain to build and keep workingVia the Dolby.io Media Enhancement add-on: a support request plus a separate Dolby API keyLoudness normalization onlyTransloadit exposes ffmpeg filters; MediaConvert does not offer speech enhancement
Your storage stays yoursSources and variants live in your buckets or on your diskYesDerived assets live with the vendorAssets live with the vendorReads and writes your buckets
Time-boxed linksexp option, signed into the URLYour framework's signed URLsExpiring delivery tokensSigned playback tokensStorage-level presigning
VideoRefused by design (415). Audio onlyWhatever you buildYes, core productYes, core productYes
What you add to your stackOne container, no database, no queueA table, a job queue, a backfillA vendor and its pricingA vendor and its pricingA vendor plus your orchestration
What it costsApache-2.0. Your compute and storageYour compute, storage, and the cruft's upkeepUsage-basedUsage-basedPer-job, usage-based

Cells reflect each product's documented public surface at the time of writing, checked against their own documentation rather than assumed. Corrections welcome: this page loses its value the moment a cell flatters us.

Against pre-generating in your app

This is the real competitor. Which one wins depends on where you start, not on which design is nicer.

Have a working setup with one small hole? Patch it. A fixed clip shape is a small model and a background job, no new runtime dependency. A team in exactly this spot told us the payoff for a nicer design is clearer once the current approach actually costs something. They were right. Revisit when it stops being cheap: variants multiplying across formats and trims, a backfill after every rule change, a variants table growing columns nobody remembers.

Nothing built yet? The comparison flips. Pre-generating is a schema, a job, and a backfill strategy, designed before you know which variants you will need. One container makes a variant a URL: a new preview shape is a changed URL, not a migration.

Either way: adopt it for the jobs you have today. Optionality you will not use yet is worth nothing.

Against Cloudinary

Cloudinary invented transformation URLs; audioproxy borrows the idea gratefully, via imgproxy. But audio is a side stage there: waveform images instead of waveform data, volume instead of loudness targets, derived assets in their storage at their prices. Want a managed everything-CDN where video leads? Cloudinary is a fine answer. Want audio-first with files in your own bucket? That is why this project exists.

Against Mux

If video is your product, use Mux. It is excellent, and audioproxy refuses video on purpose (415, before any render starts). The teams that switch are not leaving Mux the video platform. They are leaving Mux as an accidental audio pipeline: video prices for audio jobs, and no waveform data for their players.

Against batch pipelines

MediaConvert and Transloadit shine when the work is known ahead of time: transcode the archive tonight, three formats, this bucket. audioproxy is the opposite bet: render what someone asks for, when they ask, and cache it. Archive migration is a batch job. Serving listeners is not.

When audioproxy is the wrong choice

A working setup with one small hole: patch it, keep your dependency count where it is. Video in the critical path: Mux or Cloudinary. Zero appetite for running anything: a managed vendor, priced accordingly. Bulk archive conversion: a batch pipeline. We would rather you pick right for your situation than adopt early and resent the container.

Meet the unknowns before they are in your stack

The fair objection to a new dependency is not whether it holds up but what you only meet once it is in. So meet it first: the explorer on the front page runs against a real deployment, and one docker command runs the same container against your own files, read-only, in about a minute. Nothing to install in your app, nothing to de-risk later.

Render your first variantBack to the front page
Source
audioproxy/audioproxy
Elixir. Signing algorithm and reference implementations in the README.
Module docs
hexdocs.pm
The library behind the container, documented function by function.
Integration
Rails
Turns ActiveStorage attachments into signed variant URLs.
For agents
llms.txt
The contract in a form a coding agent can read in one pass.

Built by Julian Rubisch, an independent developer in Vienna. Available for Rails and Hotwire work through Rails Reviews.

Other things I make:Better Stimulus,Discover Bandcamp Labels,Crow's Nest,SoundLogo,resonAIte.