These workflows were designed for GitHub Actions and don't apply to the Gitea runner. Remove to avoid blocking the CI queue.
Go Jdenticon
A high-performance, idiomatic Go library for generating Jdenticon identicons. This library provides a simple API for creating PNG and SVG avatars, is fully configurable, and includes a command-line tool for easy generation.
It produces identical SVG output to the original Jdenticon JavaScript library.
Features
- Simple API: Generate icons with just a few lines of code
- PNG & SVG Support: Output to standard raster and vector formats
- Highly Configurable: Adjust colors, saturation, padding, and more
- Performance Optimized: Built-in LRU caching and efficient rendering
- Concurrent Batch Processing: High-performance worker pool for bulk generation
- CLI Included: Generate icons directly from your terminal
- Comprehensive Error Handling: Structured error types with actionable messages
- Identical SVG Output: Produces the same SVGs as the original JavaScript library
- High-Quality PNG Output: Visually very close PNGs (different rendering engine; differences only noticeable in side-by-side comparison)
Quick Start
This will get you generating your first icon in under a minute.
1. Installation
Library:
go get -u github.com/ungluedlabs/go-jdenticon
Command-Line Tool (Optional):
go install github.com/ungluedlabs/go-jdenticon/cmd/jdenticon@latest
2. Basic Usage (Library)
Create a file main.go and add the following code:
package main
import (
"context"
"log"
"os"
"github.com/ungluedlabs/go-jdenticon/jdenticon"
)
func main() {
// Generate an icon from a string value
icon, err := jdenticon.Generate(context.Background(), "user@example.com", 200)
if err != nil {
log.Fatalf("Failed to create icon: %v", err)
}
// Save the icon as SVG
svg, err := icon.ToSVG()
if err != nil {
log.Fatalf("Failed to generate SVG: %v", err)
}
if err := os.WriteFile("my-icon.svg", []byte(svg), 0644); err != nil {
log.Fatalf("Failed to save SVG: %v", err)
}
// Save the icon as PNG
png, err := icon.ToPNG()
if err != nil {
log.Fatalf("Failed to generate PNG: %v", err)
}
if err := os.WriteFile("my-icon.png", png, 0644); err != nil {
log.Fatalf("Failed to save PNG: %v", err)
}
log.Println("Generated my-icon.svg and my-icon.png successfully!")
// Note: SVG output is identical to JavaScript library
// PNG output is visually very close but uses a different rendering engine
}
Run the code:
go run main.go
You will find my-icon.svg and my-icon.png files in the same directory.
Advanced Usage & Configuration
The library offers fine-grained control over the icon's appearance via the Config struct and functional options.
Customizing an Icon
Here's how to generate an icon with custom colors and settings:
package main
import (
"context"
"log"
"os"
"github.com/ungluedlabs/go-jdenticon/jdenticon"
)
func main() {
// Create a custom configuration using functional options
config, err := jdenticon.Configure(
jdenticon.WithHueRestrictions([]float64{120, 180, 240}), // Greens, teals, blues only
jdenticon.WithColorSaturation(0.7), // More vivid colors
jdenticon.WithPadding(0.1), // 10% padding
jdenticon.WithBackgroundColor("#f0f0f0"), // Light gray background
)
if err != nil {
log.Fatalf("Failed to create config: %v", err)
}
// Create a generator with the custom config and caching
generator, err := jdenticon.NewGeneratorWithConfig(config, 1000)
if err != nil {
log.Fatalf("Failed to create generator: %v", err)
}
// Generate the icon
icon, err := generator.Generate(context.Background(), "custom-identifier", 200)
if err != nil {
log.Fatalf("Failed to generate icon: %v", err)
}
// Save as SVG
svg, err := icon.ToSVG()
if err != nil {
log.Fatalf("Failed to generate SVG: %v", err)
}
if err := os.WriteFile("custom-icon.svg", []byte(svg), 0644); err != nil {
log.Fatalf("Failed to save SVG: %v", err)
}
log.Println("Generated custom-icon.svg successfully!")
}
Package-Level Functions
For simple use cases, you can use the package-level functions:
// Generate SVG directly
svg, err := jdenticon.ToSVG(context.Background(), "user@example.com", 200)
if err != nil {
log.Fatal(err)
}
// Generate PNG directly
png, err := jdenticon.ToPNG(context.Background(), "user@example.com", 200)
if err != nil {
log.Fatal(err)
}
// With custom configuration
config := jdenticon.Config{
ColorSaturation: 0.8,
Padding: 0.15,
}
generator, _ := jdenticon.NewGeneratorWithConfig(config, 100)
icon, _ := generator.Generate(context.Background(), "user@example.com", 200)
For a full list of configuration options, please see the GoDoc for the Config struct.
Concurrency & Performance
Thread Safety
All public functions and types are safe for concurrent use by multiple goroutines. The library achieves thread safety through several mechanisms:
- Immutable Icons: Once created, Icon instances are read-only and can be safely shared across goroutines
- Thread-Safe Caching: Internal LRU cache uses RWMutex for optimal concurrent read performance
- Atomic Operations: Performance metrics use lock-free atomic counters
- Protected State: All shared mutable state is properly synchronized
Concurrent Usage Patterns
✅ Recommended: Reuse Generator Instances
// Create one generator and share it across goroutines
generator, err := jdenticon.NewGeneratorWithConfig(config, 1000)
if err != nil {
log.Fatal(err)
}
// Safe: Multiple goroutines can use the same generator
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
userID := fmt.Sprintf("user-%d@example.com", id)
icon, err := generator.Generate(context.Background(), userID, 64)
if err != nil {
log.Printf("Failed to generate icon: %v", err)
return
}
// Icons are immutable and safe to share
svg, _ := icon.ToSVG()
// Use svg...
}(i)
}
wg.Wait()
✅ Also Safe: Package-Level Functions
// Safe: Uses internal singleton with caching
var wg sync.WaitGroup
for _, email := range emails {
wg.Add(1)
go func(e string) {
defer wg.Done()
icon, _ := jdenticon.Generate(context.Background(), e, 64)
// Process icon...
}(email)
}
wg.Wait()
Performance Recommendations
- Larger Cache Sizes: Use 1000+ entries for high-concurrency scenarios
- Generator Reuse: Create one generator per configuration, share across goroutines
- Icon Sharing: Generated icons are immutable and efficient to share
- Monitor Metrics: Use
GetCacheMetrics()to track cache performance
// Example: Monitor cache performance
hits, misses := generator.GetCacheMetrics()
ratio := float64(hits) / float64(hits + misses) * 100
log.Printf("Cache hit ratio: %.1f%%", ratio)
Benchmarks
The library is optimized for concurrent workloads:
- Single-threaded: ~15,000 icons/second (64x64 PNG)
- Concurrent (8 cores): ~85,000 icons/second with 99%+ cache hits
- Memory efficient: ~2.1 KB per operation with LRU caching
Run benchmarks locally:
go test -bench=. -benchmem ./...
Command-Line Interface (CLI)
The jdenticon tool supports both single icon generation and high-performance batch processing.
Single Icon Generation
Generate a default 200x200 SVG icon:
jdenticon generate "my-cli-icon" > my-cli-icon.svg
Generate a 256x256 PNG icon:
jdenticon generate "my-cli-icon" -s 256 -f png -o my-cli-icon.png
Batch Processing
Generate multiple icons from a text file (one value per line):
jdenticon batch users.txt --output-dir ./avatars
High-performance concurrent batch processing:
jdenticon batch large-list.txt --output-dir ./avatars --concurrency 8 --format png --size 128
Key batch features:
- Concurrent Processing: Uses worker pool pattern for optimal performance
- Configurable Workers:
--concurrencyflag (defaults to CPU count) - Progress Tracking: Real-time progress bar with completion statistics
- Graceful Shutdown: Responds to Ctrl+C for clean termination
- Performance: Up to 3-4x speedup vs sequential processing
All available options:
jdenticon --help
jdenticon batch --help
Development & Testing
This project includes comprehensive testing and validation infrastructure to ensure quality and identical SVG output.
Directory Structure
examples/
Contains practical Go usage examples demonstrating best practices:
concurrent-usage.go- Thread-safe patterns, performance optimization, and cache monitoring- Run examples:
go run examples/concurrent-usage.go - Run with race detection:
go run -race examples/concurrent-usage.go
benchmark/
Dedicated performance testing infrastructure:
- Purpose: Performance comparison between Go and JavaScript implementations
- Coverage: Speed benchmarks, memory analysis, regression testing
- Usage: Validates performance claims and catches regressions
Building and Testing
To contribute or build from source:
-
Clone the repository:
git clone https://github.com/ungluedlabs/go-jdenticon.git cd go-jdenticon -
Run tests:
go test ./... -
Run tests with race detection:
go test -race ./... -
Run benchmarks:
go test -bench=. ./... -
Build the CLI tool:
go build ./cmd/jdenticon
API Reference
Core Functions
Generate(ctx context.Context, value string, size int) (*Icon, error)- Generate an icon with default settingsToSVG(ctx context.Context, value string, size int) (string, error)- Generate SVG directlyToPNG(ctx context.Context, value string, size int) ([]byte, error)- Generate PNG directly
Generator Type
For better performance with multiple icons:
NewGenerator() (*Generator, error)- Create generator with default cache (1000 entries)NewGeneratorWithCacheSize(size int) (*Generator, error)- Create generator with custom cache sizeNewGeneratorWithConfig(config Config, cacheSize int) (*Generator, error)- Create generator with custom configGenerate(ctx context.Context, value string, size int) (*Icon, error)- Generate icon using this generator
Icon Type
ToSVG() (string, error)- Render as SVG stringToPNG() ([]byte, error)- Render as PNG byte dataToPNGWithSize(outputSize int) ([]byte, error)- Render PNG at different size
Configuration
Use functional options for flexible configuration:
config, err := jdenticon.Configure(
jdenticon.WithColorSaturation(0.7),
jdenticon.WithPadding(0.1),
jdenticon.WithBackgroundColor("#ffffff"),
jdenticon.WithHueRestrictions([]float64{0, 120, 240}),
)
Security & Input Limits
Go Jdenticon includes configurable DoS protection to prevent resource exhaustion attacks:
config := jdenticon.Config{
MaxIconSize: 4096, // Maximum icon dimension in pixels (default: 4096)
MaxInputLength: 1048576, // Maximum input string length in bytes (default: 1MB)
// ... other config options
}
// Use 0 for default limits, positive values for custom limits, -1 to disable
icon, err := jdenticon.ToSVGWithConfig(context.Background(), "user@example.com", 512, config)
Default Security Limits:
- Icon Size: 4096×4096 pixels maximum (~64MB memory usage)
- Input Length: 1MB maximum string length
- PNG Effective Size: Automatically adjusts supersampling to stay within limits
Features:
- Smart PNG Handling:
ToPNG()automatically reduces supersampling for large icons - Configurable Limits: Override defaults or disable limits entirely for special use cases
- Structured Errors: Clear error messages explain limit violations and how to resolve them
- Fail-Fast Validation: Invalid inputs are rejected before expensive operations
The Go implementation provides superior DoS protection compared to the JavaScript reference library while producing identical output.
Performance
The library includes several performance optimizations:
- LRU Caching: Generators cache generated icons for repeated use
- Efficient Rendering: Optimized SVG and PNG generation
- Reusable Generators: Create once, use many times
- Minimal Allocations: Careful memory management
Example with caching:
generator, _ := jdenticon.NewGenerator()
// These will be cached
icon1, _ := generator.Generate(context.Background(), "user1@example.com", 64)
icon2, _ := generator.Generate(context.Background(), "user2@example.com", 64)
// Check cache performance
hits, misses := generator.GetCacheMetrics()
fmt.Printf("Cache: %d hits, %d misses\n", hits, misses)
License
This project is licensed under the Elastic License 2.0 - see the LICENSE file for details.
Key points:
- Free to use, modify, and distribute
- Cannot be offered as a hosted/managed service
- Cannot remove or circumvent license key functionality
Acknowledgments
- This library is a Go port of the excellent Jdenticon by Daniel Mester
- Special thanks to the original JavaScript implementation for the visual algorithm