Pure Go encoder and decoder for the WebP image format. Zero dependencies, zero CGo.
go get github.com/deepteams/webp
Requires Go 1.24+
- Lossy encoding & decoding (VP8)
- Lossless encoding & decoding (VP8L)
- Alpha channel support (ALPH chunk with VP8L compression)
- Animation (ANIM/ANMF) with sub-frame optimization, keyframe control, mixed codec mode
- Extended format (VP8X) with ICC, EXIF, XMP metadata
- Sharp YUV conversion for high-quality chroma subsampling
- Presets for photos, pictures, drawings, icons, text
- Transparent integration with Go's
imagepackage (image.Decodejust works) - CLI tool (
gwebp) for encoding, decoding and inspecting WebP files
package main
import (
"image"
"image/png"
"os"
_ "github.com/deepteams/webp" // register WebP format
)
func main() {
// image.Decode auto-detects WebP thanks to init() registration
f, _ := os.Open("photo.webp")
defer f.Close()
img, _, _ := image.Decode(f)
out, _ := os.Create("photo.png")
defer out.Close()
png.Encode(out, img)
}package main
import (
"image"
_ "image/jpeg"
"os"
"github.com/deepteams/webp"
)
func main() {
f, _ := os.Open("photo.jpg")
defer f.Close()
img, _, _ := image.Decode(f)
out, _ := os.Create("photo.webp")
defer out.Close()
webp.Encode(out, img, &webp.EncoderOptions{
Quality: 80,
Method: 4, // 0=fast, 6=best compression
})
}webp.Encode(out, img, &webp.EncoderOptions{
Lossless: true,
Quality: 75, // controls compression effort
})package main
import (
"image"
"image/color"
"os"
"time"
"github.com/deepteams/webp/animation"
)
func main() {
out, _ := os.Create("anim.webp")
defer out.Close()
enc := animation.NewEncoder(out, 256, 256, &animation.EncodeOptions{
Quality: 80,
LoopCount: 0, // infinite loop
})
for i := 0; i < 10; i++ {
img := image.NewNRGBA(image.Rect(0, 0, 256, 256))
// ... draw frame ...
enc.AddFrame(img, 100*time.Millisecond)
}
enc.Close()
}f, _ := os.Open("image.webp")
defer f.Close()
feat, _ := webp.GetFeatures(f)
fmt.Printf("Size: %dx%d\n", feat.Width, feat.Height)
fmt.Printf("Format: %s\n", feat.Format) // "lossy", "lossless", "extended"
fmt.Printf("Alpha: %v\n", feat.HasAlpha)
fmt.Printf("Animated: %v\n", feat.HasAnimation)
fmt.Printf("Frames: %d\n", feat.FrameCount)go install github.com/deepteams/webp/cmd/gwebp@latest# JPEG/PNG to WebP (lossy, quality 80)
gwebp enc -q 80 photo.jpg -o photo.webp
# Lossless encoding
gwebp enc -lossless input.png -o output.webp
# Sharp YUV for better chroma edges
gwebp enc -q 90 -sharp_yuv photo.jpg
# Content-specific preset
gwebp enc -preset photo -q 85 landscape.jpg
# GIF to animated WebP
gwebp enc -q 75 animation.gif -o animation.webp# WebP to PNG
gwebp dec input.webp -o output.png
# Animated WebP to GIF
gwebp dec animation.webp -o animation.gifgwebp info photo.webp| Option | Type | Default | Description |
|---|---|---|---|
Lossless |
bool |
false |
VP8L lossless encoding |
Quality |
float32 |
75 |
Compression quality (0-100) |
Method |
int |
4 |
Effort level (0=fast, 6=slowest/best) |
Preset |
Preset |
Default |
Content preset (Picture, Photo, Drawing, Icon, Text) |
UseSharpYUV |
bool |
false |
Sharp RGB-to-YUV conversion |
Exact |
bool |
false |
Preserve RGB under transparent areas |
TargetSize |
int |
0 |
Target output size in bytes |
TargetPSNR |
float32 |
0 |
Target PSNR in dB |
SNSStrength |
int |
50 |
Spatial noise shaping (0-100) |
FilterStrength |
int |
60 |
Loop filter strength (0-100) |
FilterSharpness |
int |
0 |
Loop filter sharpness (0-7) |
FilterType |
int |
1 |
Filter type (0=simple, 1=strong) |
Segments |
int |
4 |
Number of segments (1-4) |
Pass |
int |
1 |
Entropy analysis passes (1-10) |
AlphaCompression |
int |
1 |
Alpha compression (0=none, 1=lossless) |
AlphaFiltering |
int |
1 |
Alpha filter (0=none, 1=fast, 2=best) |
AlphaQuality |
int |
100 |
Alpha quality (0-100) |
Benchmarked on Apple M2 Pro (arm64, 10 cores), 1536x1024 RGB image, Go 1.24.2. Median of 3 runs.
| Library | Mode | Time | B/op | Allocs | Output |
|---|---|---|---|---|---|
| deepteams/webp (Pure Go) | Lossy | 79 ms | 1.5 MB | 124 | 193 KB |
| gen2brain/webp (WASM) | Lossy | 82 ms | 18 KB | 12 | 253 KB |
| chai2010/webp (CGo) | Lossy | 108 ms | 234 KB | 4 | 209 KB |
| gen2brain/webp (WASM) | Lossless | 275 ms | 514 KB | 12 | 2,054 KB |
| deepteams/webp (Pure Go) | Lossless | 400 ms | 115 MB | 1,393 | 1,783 KB |
| nativewebp (Pure Go) | Lossless | 428 ms | 89 MB | 2,157 | 2,012 KB |
| chai2010/webp (CGo) | Lossless | 1,320 ms | 3.5 MB | 5 | 1,751 KB |
| Library | Mode | Time | B/op | Allocs |
|---|---|---|---|---|
| chai2010/webp (CGo) | Lossy | 13 ms | 7.2 MB | 24 |
| golang.org/x/image/webp | Lossy | 25 ms | 2.6 MB | 13 |
| deepteams/webp (Pure Go) | Lossy | 27 ms | 6.5 MB | 7 |
| gen2brain/webp (WASM) | Lossy | 32 ms | 1.2 MB | 41 |
| chai2010/webp (CGo) | Lossless | 32 ms | 14.7 MB | 33 |
| nativewebp (Pure Go) | Lossless | 53 ms | 6.4 MB | 50 |
| gen2brain/webp (WASM) | Lossless | 55 ms | 10.6 MB | 50 |
| golang.org/x/image/webp | Lossless | 57 ms | 7.3 MB | 1,416 |
| deepteams/webp (Pure Go) | Lossless | 58 ms | 8.5 MB | 340 |
Lossy encoding uses row-pipelined parallelism that scales with available cores. See benchmark/ for full methodology and small-image results.
cd benchmark && go test -bench=. -benchmem -count=3 -run='^$' -timeout=30mOutput files are compatible with all WebP decoders (Chrome, Firefox, Safari, libwebp dwebp, ImageMagick, etc.). The encoder produces bitstream-conformant VP8/VP8L output matching the behavior of Google's C reference implementation (libwebp).
webp.go / encode.go Public API (Decode, Encode, Options)
animation/ Animation encoder/decoder (ANIM/ANMF)
cmd/gwebp/ CLI tool
mux/ WebP mux/demux (RIFF container)
sharpyuv/ Sharp YUV color space conversion
internal/
bitio/ Bit-level I/O (boolean arithmetic, lossless streams)
container/ RIFF/WEBP container parsing
dsp/ DSP (YUV conversion, filters, prediction, cost)
lossless/ VP8L encoder/decoder
lossy/ VP8 encoder/decoder
pool/ Object pool utilities
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-change) - Run tests (
go test ./...) - Run race detector (
go test -race ./...) - Submit a pull request
- Keep zero external dependencies
- All codec changes must pass round-trip tests (encode -> decode -> verify)
- Run
go vet ./...and fix any issues before submitting - Bitstream code is precision-critical: test thoroughly against reference files
MIT License - see LICENSE for details.