If you know imgproxy, this is that, for audio. Point it at S3 or a directory, name a format in the path, and the proxy renders the variant on demand, streams it to the first requester, and writes it back for cached, range-capable serving.
Every request is a signed GET. No request bodies, no server-side state, and no configuration to add when the ask changes.
Options are ordered key:value segments. They fully describe the output and double as the cache key, so f:opus/br:96 and br:96/f:opus yield the same variant. Build one.
Option order is normalized before it is hashed, so the same set of options is the same cache key however you spell it. Simulated output: sizes and headers above are computed from the options, not measured — a live playground against a real proxy arrives with an upcoming release.
Default mp3. aac is an ADTS stream; m4a is fragmented MP4, cut on duration so it streams rather than waiting for a keyframe that audio never has.
brBitrate in kbps for lossy formats.qVBR quality on the codec's own scale. Mutually exclusive with br.srResample. Defaults to the source rate, capped at 48 kHz for lossy.chDownmix to 1 or 2.bdBit depth, lossless only: 16, 24, or 32f for wav.tTrim, start and optional duration in seconds, decimals fine.fadeIn and out, applied inside the trimmed region.gainStatic gain in dB, signed.normEBU loudness normalization, default -16:-1.5:11. v1 is single pass, which is good enough for previews.A format, not a separate resource, so it caches and redirects exactly as audio does. Min and max pairs in audiowaveform's own JSON or compact binary, ready to draw a player.
ptsPair count, default 800.pk_fmtjson or dat.chDefaults to 1 here, unlike every other format.dlSets Content-Disposition to attachment.cbCache buster, participates in the key./infoFormat, duration, sample rate, channels, bit depth, bitrate, size, tags. A field the prober cannot answer is omitted, never null./healthLiveness. 200 whatever the load, because a busy proxy is a working proxy./ready503 once queue depth hits the threshold, recovering at half of it./metricsPrometheus, on a listener of its own with bounded label values.A source carrying a genuine video stream is refused with 415 before any render starts. It is a reject, not a strip: pulling the audio track out of arbitrary video would make this a free transcoding service at video's cost profile and video's exposure, which is a different product. Embedded cover art is metadata and renders normally.
The same URL is framed differently depending on what is cached, so a client implements both. That is the whole contract, and it does not change with the storage backend or the serve mode.
200 with chunked framing and no Content-Length. Bytes leave as the encoder produces them and are teed to the variant store at the same time. Concurrent requests for one cache key coalesce onto a single render and all subscribe to its stream.
By default a hit is a 302 to a presigned variant URL, and storage serves the ranges. In proxy mode the proxy relays the object itself with Content-Length and Accept-Ranges. Either way seeking works, which is the part players care about.
A variant is fully described by its options, so it can never go stale. Every response states its own cache policy, success or error, and no CDN default gets to decide retention.
Point the container at a directory of real files. It runs unsigned here, with the literal insecure standing in for a signature, which is the mode meant for exactly this first look and nothing more.
docker run --rm -p 4000:4000 \ -e AP_ALLOW_INSECURE=true \ -e AP_LOCAL_ROOT=/audio \ -v /path/to/your/audio:/audio:ro \ ghcr.io/audioproxy/audioproxy:0.4.0
BASE=localhost:4000 SRC='plain/local://track.wav' # 30 s Opus preview at 96 kbps, faded curl -o preview.opus \ "$BASE/insecure/f:opus/br:96/t:0:30/fade:1:1/$SRC" # Waveform min/max pairs for a player UI curl "$BASE/insecure/f:peaks/$SRC" # Duration, sample rate, channels curl "$BASE/insecure/info/$SRC"
The mount is read only on purpose: write access to the root is equivalent to choosing what the proxy will serve, so nothing should have it. Replace insecure mode with signed URLs before anything faces real traffic.