Skip to content

Brotli

Brotli is a modern compression algorithm developed by Google, optimized for web content. It achieves the best compression ratios among compressionz codecs.

PropertyValue
DeveloperGoogle
First Release2015
ImplementationVendored libbrotli
LicenseMIT
LevelCompressDecompressRatio
fast1.3 GB/s1.9 GB/s99.9%
default1.3 GB/s1.8 GB/s99.9%
best86 MB/s1.5 GB/s99.9%+
  • ✅ Excellent compression ratios
  • ✅ Streaming support
  • ✅ Supported by modern browsers
  • ✅ Multiple compression levels
  • ❌ No magic bytes (can’t auto-detect)
  • ❌ No dictionary support (in this API)
  • ❌ No built-in checksum
const cz = @import("compressionz");
// Compress
const compressed = try cz.compress(.brotli, data, allocator);
defer allocator.free(compressed);
// Decompress
const decompressed = try cz.decompress(.brotli, compressed, allocator);
defer allocator.free(decompressed);

Brotli’s levels have a significant impact on both speed and ratio:

// Fast - suitable for dynamic content
const fast = try cz.compressWithOptions(.brotli, data, allocator, .{
.level = .fast,
});
// Default - balanced
const default = try cz.compressWithOptions(.brotli, data, allocator, .{
.level = .default,
});
// Best - maximum compression for static assets
const best = try cz.compressWithOptions(.brotli, data, allocator, .{
.level = .best,
});
LevelCompressDecompressRatioUse Case
fast1.3 GB/s1.9 GB/s99.9%Dynamic content
default1.3 GB/s1.8 GB/s99.9%General use
best86 MB/s1.5 GB/s99.9%+Static assets

Key insight: best level is ~15× slower than fast, but produces the smallest files. Use best only for one-time compression (static files).

Brotli supports streaming for large files:

const cz = @import("compressionz");
const std = @import("std");
pub fn compressToBrotli(allocator: std.mem.Allocator, input_path: []const u8, output_path: []const u8) !void {
const input = try std.fs.cwd().openFile(input_path, .{});
defer input.close();
const output = try std.fs.cwd().createFile(output_path, .{});
defer output.close();
var comp = try cz.compressor(.brotli, allocator, output.writer(), .{
.level = .best, // Worth the time for static files
});
defer comp.deinit();
var buf: [65536]u8 = undefined;
while (true) {
const n = try input.read(&buf);
if (n == 0) break;
try comp.writer().writeAll(buf[0..n]);
}
try comp.finish();
}
pub fn decompressBrotli(allocator: std.mem.Allocator, path: []const u8) ![]u8 {
const file = try std.fs.cwd().openFile(path, .{});
defer file.close();
var decomp = try cz.decompressor(.brotli, allocator, file.reader());
defer decomp.deinit();
return decomp.reader().readAllAlloc(allocator, 1024 * 1024 * 1024);
}
const cz = @import("compressionz");
// Compress with level
const compressed = try cz.brotli.compress(data, allocator, .best);
defer allocator.free(compressed);
// Decompress
const decompressed = try cz.brotli.decompress(compressed, allocator);
defer allocator.free(decompressed);
// With size limit
const limited = try cz.brotli.decompressWithLimit(compressed, allocator, max_size);

Brotli was specifically designed for web content:

Brotli includes a 120 KB static dictionary with common web content:

  • HTML tags (<div>, <span>, <script>)
  • CSS properties (background-color, font-family)
  • JavaScript keywords (function, return, undefined)
  • Common words and phrases

This dramatically improves compression for typical web content.

BrowserSupport
Chrome✅ Since v49
Firefox✅ Since v44
Safari✅ Since v11
Edge✅ Since v15
IE

~95% of browsers support Brotli.

Accept-Encoding: gzip, deflate, br
Content-Encoding: br

Ideal workflow for static web assets:

const std = @import("std");
const cz = @import("compressionz");
pub fn precompressAssets(allocator: std.mem.Allocator, assets_dir: []const u8) !void {
var dir = try std.fs.cwd().openDir(assets_dir, .{ .iterate = true });
defer dir.close();
var iter = dir.iterate();
while (try iter.next()) |entry| {
if (entry.kind != .file) continue;
// Skip already compressed
if (std.mem.endsWith(u8, entry.name, ".br")) continue;
if (std.mem.endsWith(u8, entry.name, ".gz")) continue;
const ext = std.fs.path.extension(entry.name);
const should_compress = std.mem.eql(u8, ext, ".html") or
std.mem.eql(u8, ext, ".css") or
std.mem.eql(u8, ext, ".js") or
std.mem.eql(u8, ext, ".json") or
std.mem.eql(u8, ext, ".svg");
if (should_compress) {
const input_path = try std.fs.path.join(allocator, &.{ assets_dir, entry.name });
defer allocator.free(input_path);
const data = try std.fs.cwd().readFileAlloc(allocator, input_path, 10 * 1024 * 1024);
defer allocator.free(data);
// Compress with best level (one-time cost)
const compressed = try cz.compressWithOptions(.brotli, data, allocator, .{
.level = .best,
});
defer allocator.free(compressed);
const output_path = try std.fmt.allocPrint(allocator, "{s}.br", .{input_path});
defer allocator.free(output_path);
try std.fs.cwd().writeFile(output_path, compressed);
std.debug.print("{s}: {d} -> {d} bytes ({d:.1}%)\n", .{
entry.name,
data.len,
compressed.len,
@as(f64, @floatFromInt(compressed.len)) / @as(f64, @floatFromInt(data.len)) * 100,
});
}
}
}

Unlike most formats, Brotli has no magic number:

// Cannot auto-detect Brotli
const codec = cz.Codec.detect(data); // Returns null for Brotli
// Must know it's Brotli beforehand
const decompressed = try cz.decompress(.brotli, data, allocator);

Workaround: Use file extensions (.br) or content metadata to identify Brotli data.

Brotli uses a meta-block structure:

┌─────────────────────────────────────────────────────┐
│ Meta-block header │
│ - ISLAST flag │
│ - MLEN (meta-block length) │
│ - Compressed/uncompressed flag │
├─────────────────────────────────────────────────────┤
│ Compressed data (if compressed) │
│ - Literal insert-and-copy commands │
│ - Distance codes │
│ - Context modeling │
├─────────────────────────────────────────────────────┤
│ ... more meta-blocks ... │
├─────────────────────────────────────────────────────┤
│ Final meta-block (ISLAST=1) │
└─────────────────────────────────────────────────────┘

Brotli combines multiple techniques:

  1. LZ77 matching — Find repeated sequences
  2. 2nd-order context modeling — Predict based on previous bytes
  3. Static dictionary — 120 KB of common web strings
  4. Asymmetric numeral systems (ANS) — Efficient entropy coding
  • Larger window size (up to 16 MB vs Gzip’s 32 KB)
  • Better context modeling
  • Optimized for text/web content
  • Built-in dictionary for common patterns
MetricBrotli (best)Gzip (best)
Compress86 MB/s691 MB/s
Decompress1.5 GB/s2.9 GB/s
Ratio99.9%+99.6%
Browser~95%100%
Web-optimizedYesNo
Content TypeGzipBrotliSavings
HTML20%17%15% smaller
CSS15%12%20% smaller
JavaScript22%18%18% smaller
JSON10%8%20% smaller

Best for:

  • Static web assets (CSS, JS, HTML)
  • CDN content
  • Pre-compressed files
  • When bandwidth > CPU cost

Not ideal for:

  • Dynamic content (use Gzip)
  • Real-time compression (use LZ4/Snappy)
  • General purpose (use Zstd)
  • Legacy browser support (use Gzip)

Brotli uses more memory than simpler algorithms:

OperationMemory
Compression~1 MB
Decompression~256 KB

Consider this for memory-constrained environments.