blurhash

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 22, 2026 License: MIT Imports: 10 Imported by: 0

README

BlurHash Go Library

Go Reference Go Report Card

A fast, efficient, and pure Go implementation of the BlurHash image encoding algorithm. BlurHash is a compact representation of a placeholder for an image, allowing you to show a smooth placeholder while loading images.

Features

  • 🚀 Fast & Efficient - Pure Go with zero external dependencies
  • 📦 Spec Compliant - Matches official implementations (C, TypeScript, Swift)
  • 🎨 Flexible - Configurable component counts, custom punch factors, and auto-sizing
  • 🧪 Well Tested - Comprehensive test suite with real images and cross-language validation
  • 📊 Streaming Support - Read/write images directly from files or buffers
  • Optimized - Pre-computed lookup tables, minimal allocations
  • 🛠️ CLI Tool - Command-line utility for encoding/decoding images
  • 📖 Full Documentation - Clear examples and API reference

Installation

go get github.com/moeeinaali/go-blurhash

Quick Start

Encoding an Image
package main

import (
	"fmt"
	"image"
	_ "image/jpeg"
	"os"

	"github.com/moeeinaali/go-blurhash"
)

func main() {
	// Open and decode an image
	f, _ := os.Open("photo.jpg")
	img, _, _ := image.Decode(f)
	f.Close()

	// Encode to BlurHash
	hash, _ := blurhash.Encode(img)
	fmt.Println(hash) // e.g., "B~LrYI~c{H?b=::k"
}
Decoding a Hash to an Image
hash := "B~LrYI~c{H?b=::k"

// Decode to a specific size
img, _ := blurhash.Decode(hash, 256, 256)

// Save as PNG
f, _ := os.Create("preview.png")
defer f.Close()
png.Encode(f, img)
Validating a Hash
isValid, reason := blurhash.IsValid("B~LrYI~c{H?b=::k")
if !isValid {
	fmt.Printf("Invalid hash: %s\n", reason)
}

API Reference

Encoding
// Encode image with default options (4x3 components)
hash, err := blurhash.Encode(img)

// Encode with custom components
hash, err := blurhash.Encode(img, blurhash.WithComponents(6, 5))

// Auto-detect components based on image aspect ratio
hash, err := blurhash.Encode(img, blurhash.WithAutoComponents())

// Limit image size before encoding (useful for large images)
hash, err := blurhash.Encode(img, blurhash.WithMaxSize(100))

// Combine options
hash, err := blurhash.Encode(img,
	blurhash.WithComponents(4, 3),
	blurhash.WithMaxSize(200),
)
Decoding
// Decode to specific dimensions
img, err := blurhash.Decode(hash, width, height)

// Decode with custom punch (contrast multiplier)
img, err := blurhash.Decode(hash, width, height, blurhash.WithPunch(1.5))

// Combine options
img, err := blurhash.Decode(hash, width, height,
	blurhash.WithPunch(1.2),
)
Validation
// Check if a hash is valid
isValid, reason := blurhash.IsValid(hash)

// Examples of reasons:
// - "valid"
// - "hash too short (minimum 6 characters)"
// - "invalid character: !"
// - "error decoding components"
Streaming
// Encode from a file reader
f, _ := os.Open("image.jpg")
defer f.Close()
hash, err := blurhash.EncodeReader(f)

// Decode to a file writer
f, _ := os.Create("preview.png")
defer f.Close()
err := blurhash.DecodeToWriter(f, hash, 256, 256)
Reusable Encoders/Decoders

For better performance when encoding/decoding multiple images, use structured instances:

// Create an encoder with default options
encoder := blurhash.NewEncoder(
	blurhash.WithAutoComponents(),
	blurhash.WithMaxSize(100),
)

// Reuse for multiple images
for _, imgPath := range imageList {
	f, _ := os.Open(imgPath)
	img, _, _ := image.Decode(f)
	hash, _ := encoder.Encode(img)
	fmt.Println(hash)
	f.Close()
}

// Create a decoder
decoder := blurhash.NewDecoder(blurhash.WithPunch(1.2))

// Reuse for multiple hashes
for _, hash := range hashList {
	img, _ := decoder.Decode(hash, 256, 256)
	// Process img
}

Options

Option Type Default Purpose
WithComponents(x, y) int, int 4, 3 Set component counts (each 1-9)
WithAutoComponents() - - Auto-detect components based on aspect ratio
WithMaxSize(pixels) int 32 Maximum image dimension before downscaling
WithPunch(factor) float64 1.0 Contrast multiplier during decoding (>0)

CLI Tool

Build the command-line tool:

go build -o blurhash ./cmd/blurhash
Encoding
# Encode with default settings (4x3 components)
./blurhash encode photo.jpg

# Encode with custom components
./blurhash encode -x 6 -y 5 photo.jpg

# Auto-detect components
./blurhash encode -auto photo.jpg

# Save hash to file
./blurhash encode photo.jpg -out hash.txt

# With max size
./blurhash encode -maxsize 256 large-photo.jpg
Decoding
# Decode to 256x256 PNG
./blurhash decode "B~LrYI~c{H?b=::k"

# Custom output size
./blurhash decode -w 512 -h 512 "B~LrYI~c{H?b=::k" -out large.png

# With punch factor
./blurhash decode -punch 1.5 "hash" -out punchy.png
Validation
# Validate one hash
./blurhash validate "B~LrYI~c{H?b=::k"

# Validate multiple hashes
./blurhash validate "hash1" "hash2" "hash3"

Examples

See examples/example.go for more detailed usage examples:

go run examples/example.go

Topics covered:

  • Basic encode/decode operations
  • Using encoding options
  • Working with file I/O
  • Hash validation and error handling
  • Round-trip testing

Performance

Benchmarks on Apple M4 (256×256 image):

BenchmarkEncode256     1036 ns/op    7.6 KB/op    1043 allocs/op
BenchmarkDecode256    84.8 µs/op    6.6 KB/op      24 allocs/op

Performance characteristics:

  • Encoding: O(width × height × components) - typically 1-2ms for 256×256 images
  • Decoding: O(width × height × components) - typically 50-200µs for 256×256 output
  • Memory: Minimal allocations; reusable buffers when using Encoder/Decoder
  • Downscaling: Images larger than maxSize are downscaled before encoding, reducing computation

Testing

Comprehensive test suite includes:

  • Unit tests - Base83 encoding, color conversions, DCT, component validation
  • Golden tests - Output verification against official C implementation
  • Cross-language vectors - Validation against C and TypeScript implementations
  • Integration tests - Real image files (solid colors, gradients, photographs)
  • Component variations - 1×1 through 9×9 configurations
  • Fuzz tests - Random inputs to ensure robustness
  • Stress tests - 100+ encode/decode cycles
  • Benchmarks - Performance measurement suite
  • Streaming - File I/O operations

Run tests:

go test ./...              # All tests
go test -run Integration   # Integration tests only
go test -bench .           # Benchmarks
go test -fuzz Fuzz         # Fuzz tests

Technical Details

Supported Image Formats

Encoding accepts any image.Image:

  • PNG, JPEG, GIF (via standard library)
  • Custom image formats (if they implement image.Image)

Decoding produces:

  • image.RGBA - 32-bit RGBA image
Component Count

Component counts determine hash length and quality:

  • More components = longer hash, higher quality
  • X and Y are independent: 4×3, 6×5, 9×9 are all valid
  • Minimum 1×1, maximum 9×9 for each dimension

Hash length formula: 4 + 2×(compX×compY)

Examples:

  • 1×1: 6 characters
  • 4×3: 28 characters
  • 6×5: 64 characters
  • 9×9: 166 characters
Color Space

The algorithm uses linear RGB internally:

  • sRGB input (0-255 uint8) → linear RGB (0-1 float)
  • DCT applied in linear RGB
  • linear RGB → sRGB output (0-255 uint8)

This ensures perceptually accurate results.

Comparison with Alternatives

Feature blurhash-go Official C TypeScript Swift
Language Go C TypeScript/JS Swift
Dependencies None None None None
Performance ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Spec Compliant
Test Suite -

Error Handling

The library provides descriptive errors:

hash, err := blurhash.Encode(img, blurhash.WithComponents(10, 3))
// Error: invalid component count (outside 1-9 range)

img, err := blurhash.Decode("ab", 256, 256)
// Error: hash too short (minimum 6 characters)

img, err := blurhash.Decode("!!!!!!!", 256, 256)
// Error: invalid character in hash

Streaming Examples

Encode from HTTP response
resp, _ := http.Get("https://example.com/photo.jpg")
defer resp.Body.Close()

hash, err := blurhash.EncodeReader(resp.Body)
Decode to HTTP response
w.Header().Set("Content-Type", "image/png")
blurhash.DecodeToWriter(w, hash, 256, 256)

Contributing

Contributions welcome! Areas of interest:

  • Performance optimizations
  • Additional image format support
  • More comprehensive examples
  • Documentation improvements
  • Bug reports and fixes

Please ensure:

  • Tests pass: go test ./...
  • Code follows conventions: gofmt -w .
  • No new external dependencies without discussion
  • Commit messages follow Conventional Commits

Algorithm Reference

License

MIT License - see LICENSE file for details

Acknowledgments

  • BlurHash - Original algorithm and specification by Wolt
  • Reference implementations in C, TypeScript, and Swift
  • The Go community for standard library excellence

Changelog

v1.0.0 (March 2026)
  • ✅ Full BlurHash implementation
  • ✅ Encode/Decode API with options pattern
  • ✅ CLI tool for command-line usage
  • ✅ Streaming support for file I/O
  • ✅ Comprehensive test suite
  • ✅ Validation utilities
  • ✅ Performance benchmarks
  • ✅ Complete documentation

Future Roadmap

  • WASM bindings for web use
  • WebP and other format support
  • Batch processing optimizations
  • Hardware acceleration (SIMD)
  • Integration examples (web frameworks)
  • Metrics and profiling utilities

FAQ

Q: How does BlurHash differ from simple thumbnails? A: BlurHash creates a compact (6-200 character) hash that decodes to any size smoothly, while thumbnails are full images. BlurHash is ideal for placeholders during image loading.

Q: Can I use a BlurHash from another language? A: Yes! BlurHash is standardized. Hashes created by Python/JavaScript/Swift libraries work with this Go implementation and vice versa.

Q: What's the recommended component count? A: Start with 4×3 (28 characters) for balanced quality/size. Use 6×5 (64 chars) for higher quality, 1×1 (6 chars) for minimal size.

Q: Does it work with animated images (GIF)? A: Yes, but only the first frame is encoded. The library accepts any image.Image, including GIF frames.

Q: Can I use this in production? A: Absolutely! The library is fully tested, spec-compliant, and handles all inputs safely (no panics).


Made with ❤️ for the Go community

Questions? Open an issue or start a discussion.

Generated Output Matrix

This section is auto-generated by go run ./scripts/generate_readme_output_matrix.go.

Inputs:

  • files: testdata/1.jpg, testdata/2.jpg
  • sizes (width=height): 8, 128
  • components: x=1 y=1, x=9 y=1, x=1 y=9, x=9 y=9

Rendering notes:

  • each image is decoded at its real size (8x8 or 128x128)
  • each image is displayed in README at 300x300 pixels
  • image src points to generated PNG files in docs/generated/output-matrix
testdata/1.jpg

Original image (real size: 1170x658, display: 300x300)

original testdata/1.jpg
size=8 (real output: 8x8, display: 300x300)
row-1
hash length: 6
hash length: 22
hash length: 22
hash length: 166
Casex=1 y=1x=9 y=1x=1 y=9x=9 y=9
size=128 (real output: 128x128, display: 300x300)
row-1
hash length: 6
hash length: 22
hash length: 22
hash length: 166
Casex=1 y=1x=9 y=1x=1 y=9x=9 y=9
testdata/2.jpg

Original image (real size: 1920x1200, display: 300x300)

original testdata/2.jpg
size=8 (real output: 8x8, display: 300x300)
row-1
hash length: 6
hash length: 22
hash length: 22
hash length: 166
Casex=1 y=1x=9 y=1x=1 y=9x=9 y=9
size=128 (real output: 128x128, display: 300x300)
row-1
hash length: 6
hash length: 22
hash length: 22
hash length: 166
Casex=1 y=1x=9 y=1x=1 y=9x=9 y=9

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidComponents indicates component counts outside valid range [1,9].
	ErrInvalidComponents = errors.New("blurhash: components must be between 1 and 9")
	// ErrMismatchedImageSize indicates inconsistent image bounds.
	ErrMismatchedImageSize = errors.New("blurhash: invalid image bounds")
	// ErrHashTooShort indicates an invalid hash shorter than minimum length.
	ErrHashTooShort = errors.New("blurhash: hash must be at least 6 characters")
	// ErrHashLengthMismatch indicates hash length does not match encoded component counts.
	ErrHashLengthMismatch = errors.New("blurhash: hash length mismatch for encoded component counts")
	// ErrInvalidDimensions indicates invalid output dimensions for decoding.
	ErrInvalidDimensions = errors.New("blurhash: width and height must be > 0")
	// ErrInvalidCharacter indicates unsupported characters in base83 strings.
	ErrInvalidCharacter = errors.New("blurhash: invalid base83 character")
)

Functions

func Decode

func Decode(hash string, width, height int, opts ...Option) (image.Image, error)

Decode reconstructs an image from a BlurHash string.

func DecodeToWriter

func DecodeToWriter(w io.Writer, hash string, width, height int, opts ...Option) error

DecodeToWriter decodes a BlurHash and writes it as PNG to a writer.

func Encode

func Encode(img image.Image, opts ...Option) (string, error)

Encode produces a BlurHash string for an image.

func EncodeReader

func EncodeReader(r io.Reader, opts ...Option) (string, error)

EncodeReader decodes an image from a reader and returns its BlurHash.

func IsValid

func IsValid(hash string) (bool, string)

IsValid returns validation state and reason for an invalid blurhash string.

Types

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

Decoder decodes blurhash strings into images.

func NewDecoder

func NewDecoder(opts ...Option) *Decoder

NewDecoder constructs a decoder with options.

func (*Decoder) Decode

func (d *Decoder) Decode(hash string, width, height int) (image.Image, error)

Decode decodes a blurhash string into an RGBA image.

func (*Decoder) DecodeToWriter

func (d *Decoder) DecodeToWriter(w io.Writer, hash string, width, height int) error

DecodeToWriter decodes a BlurHash and writes it as PNG to a writer.

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

Encoder encodes images into blurhash strings.

func NewEncoder

func NewEncoder(opts ...Option) *Encoder

NewEncoder constructs an encoder with options.

func (*Encoder) Encode

func (e *Encoder) Encode(img image.Image) (string, error)

Encode encodes an image into a BlurHash string.

func (*Encoder) EncodeReader

func (e *Encoder) EncodeReader(r io.Reader) (string, error)

EncodeReader decodes an image from a reader and returns its BlurHash.

type Option

type Option func(*options)

Option configures encoding/decoding behavior.

func WithAutoComponents

func WithAutoComponents() Option

WithAutoComponents enables automatic component selection for encoding.

func WithComponents

func WithComponents(x, y int) Option

WithComponents sets explicit BlurHash component counts for encoding.

func WithMaxSize

func WithMaxSize(size int) Option

WithMaxSize sets the maximum image side used during encoding downscale.

func WithPunch

func WithPunch(p float64) Option

WithPunch sets the decode punch factor (contrast multiplier for AC terms).

Directories

Path Synopsis
cmd
blurhash command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL