BOiNK SFX Synth can export what it makes as WAV, MP3, FLAC and (on devices that support it) AAC. The first three were easy: the browser or a small library already does the work. FLAC was the awkward one. Browsers can play FLAC, but none of them can encode it — WebCodecs has no FLAC encoder. So an app that wants to save a FLAC file has to bring its own.

The options we found — libflac.js, @mediabunny/flac-encoder, flac.wasm — are all the reference FLAC library compiled to WebAssembly. That works, and it is fast, but it means shipping a binary alongside the page and loading it at run time. We wanted the export code to be one plain script with nothing to fetch. So we wrote an encoder from scratch, shipped it inside BOiNK, and now we are releasing it for anyone to use: microflac.js, MIT licensed, on GitHub.

Using it

<script src="microflac.js"></script>
<script>
  const flac = microflac.encode({ channels: [left, right], sampleRate: 44100, bitsPerSample: 16 });
  const blob = new Blob([flac], { type: "audio/flac" });   // flac is a Uint8Array
</script>

It takes one typed array per channel (or one interleaved array), floats from −1 to 1 or integers, and returns the finished file. There is an async version that yields to the event loop between blocks, a streaming encoder for feeding audio in pieces, and an ES-module build. It has no DOM access, so it also runs inside a Web Worker and in Node.

What it does

The output is a valid FLAC stream with a proper header — including the MD5 checksum of the original audio — and checksums on every frame. The default setting squeezes hard: for each 4096-sample block it tries all four stereo modes and keeps whichever comes out smallest, tries a range of predictors up to order 12 over six different analysis windows, searches the precision of the predictor coefficients, and finds the best entropy-coding partitioning. A "fast" setting trades a little size for speed.

microflac encoder pipeline Audio samples are split into 4096-sample blocks. For each block the encoder tries four stereo modes, then for each channel picks the smallest of constant, verbatim, fixed-predictor and linear-prediction subframes, searches the entropy-coding partitions, and writes a frame with checksums. A stream header with an MD5 of the audio comes first. PCM samples8 / 16 / 24 bit 4096-sampleblocks Stereo modeleft/right, left/side,right/side, mid/side Per channel: smallest wins constant · verbatimfixed predictor, order 0–4linear prediction, order up to 12, 6 windows Entropy codingRice partition search,escape codes Frame + CRC-8 / CRC-16repeated for every block Stream header (STREAMINFO)includes MD5 of the original audio
Figure 1. What the encoder does with each block of audio. Every choice is made by actually encoding the options and keeping the smallest result.

The numbers

All measured in Chromium on one Windows PC; yours will differ.

WhatResult
Code size29.8 KB as shipped (with licence and comments); about 24 KB stripped of comments; 9.9 KB gzipped (8.0 KB stripped and gzipped)
File size vs. libFLAC's maximum preset (WebAssembly build)Smaller on all 24 test files, 0.904 to 0.999 of libFLAC's size, 0.982 overall (about 2% smaller on average, up to about 10% on a few clips)
Speed, 10 s of 44.1 kHz stereoabout 2.2 s at the default setting, about 0.34 s at "fast" (files about 1% larger)
Speed of libFLAC-in-WebAssembly, same kind of clipabout 0.13 s — roughly 17 times faster than microflac's default
File size compared with libFLAC at its maximum preset libFLAC at its maximum preset is the baseline at 1.000. microflac's default setting is 0.982 of that size overall, with individual test files ranging from 0.904 to 0.999. File size relative to libFLAC maximum preset (1.000), smaller is better. Axis starts at 0.85. 0.850.900.951.001.05 libFLAC = 1.000 0.904 (best file) 0.982 overall 0.999 (worst file)
Figure 2. Output size on our 24 test files (BOiNK's own short sound effects, not a music corpus) compared with libFLAC's maximum preset built as WebAssembly. The dot is the overall ratio; the line spans the best and worst single file.
Time to encode 10 seconds of 44.1 kHz stereo libFLAC built as WebAssembly took about 0.13 seconds. microflac's fast setting took about 0.34 seconds and its default setting about 2.2 seconds. Time to encode 10 s of 44.1 kHz stereo (Chromium, one Windows PC). Shorter is faster. libFLAC (WebAssembly)0.13 s microflac, "fast"0.34 s microflac, default2.2 s
Figure 3. Speed, drawn to scale. The default setting does an exhaustive search for the smallest file, so it is slower; "fast" gives up about 1% in size for roughly six times the speed.

So this is not a speed story. If you encode long files on the main thread, use a Worker or the async API; for short sound effects, like BOiNK's, 2 seconds for ten seconds of stereo is fine, and the files come out a little smaller.

How we tested it — and what we have not

The test page in the download runs 311 checks in your own browser, offline. Every file is checked three ways: by a separate reference decoder written for the page (bit-exact, and it verifies every checksum and header field), against a second, independently written MD5, and by the browser's own decoder. The cases cover silence, a full-scale square wave, white noise and sine waves; lengths of 1, 2, 4095, 4096, 4097 and 4608 samples; mono and stereo; 8, 16 and 24 bit; nine sample rates; three and six channels; and the streaming, async and ES-module paths. Everything passed.

The honest limits: it is an encoder only, and the file contains just the required header block — no tags, seek table or cover art. It handles 8, 16 and 24 bit and up to 8 channels (we tested 1, 2, 3 and 6). We compared sizes on BOiNK's own short sound-effect clips, not on a large independent music corpus, and we checked decoding with Chromium's decoder and our reference decoder — not with the flac command-line tool or a range of players. If you find a file it gets wrong, we want to know.

Layout of a FLAC file A FLAC file starts with the four-byte marker fLaC and a STREAMINFO block holding sample rate, channels, bit depth, length and an MD5 of the audio. After that come frames. Each frame has a header with its own CRC-8, then one subframe per channel, then a CRC-16 footer. A FLAC file, start to end (not to scale) fLaC STREAMINFOrate · channels · bits · length · MD5 Frame 1 · Frame 2 · … · Frame N Inside one frame Headerblock size · rate · stereo mode · CRC-8 One subframe per channelpredictor type · coefficients · Rice-coded residual CRC-16whole frame
Figure 4. The structure microflac writes. Every frame is independently checkable, and the MD5 in the header lets any decoder confirm it got exactly the original audio back.

Download

The source is on GitHub: github.com/dialersapps/microflac (issues and pull requests welcome). The downloads below are plain files with nothing to build, no tracking and no outside requests:

  • microflac-1.0.0.zip — everything below in one file (encoder, ES module, test page, README, licence)
  • microflac.js — the encoder (works with a plain script tag, require() and Workers)
  • microflac.mjs — the same code as an ES module
  • test.html — the in-browser test suite (serve the folder over http to run every check)
  • README.md — API, formats, limits
  • LICENSE — MIT, copyright Dialer's Apps 2026

The code was written from scratch against the FLAC specification; it is not derived from libFLAC or any other encoder.