mirror of
https://github.com/OffworldNexus/bird-id.git
synced 2026-11-08 21:05:58 +01:00
Deterministic Saul Bass bird avatars generated from an email address in Rust
- Rust 100%
Provide a zero-dependency library and CLI tool that deterministically generates geometric mid-century bird avatars in SVG format from email addresses: - Include 20 hand-crafted 512x512 SVG bird-head templates inscribed within the r = 256 circle - Implement email normalization and 64-bit FNV-1a + SplitMix64 hashing - Implement OKLab/OKLCH color space conversions with binary-search sRGB gamut mapping and 8 mid-century silkscreen colorway families - Support both square and circle-clipped SVG rendering via the Rust API and birdid CLI - Add unit, integration, and doc tests, README documentation, showcase image, Apache-2.0 license, and GitHub Actions CI |
||
|---|---|---|
| .github/workflows | ||
| docs | ||
| src | ||
| templates | ||
| tests | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE | ||
| README.md | ||
bird-id
Deterministic Saul Bass airline-poster bird avatars generated from an email address in pure, zero-dependency Rust.
Given any email address, bird-id:
- Normalises & hashes the email into a 64-bit value (
FNV-1a+SplitMix64), folding case, whitespace,mailto:prefixes, andDisplay Name <user@domain>wrappers so equivalent addresses always map to the same bird. - Selects one of 20 hand-crafted SVG bird-head models (
01-finchthrough20-quail). Every model is built in a512×512coordinate space with all bird geometry strictly inside the inscribedr = 256circle so circular cropping never clips a beak, crest, or plume. - Computes a mid-century silkscreen colour palette in OKLCH (
src/oklch.rs,src/palette.rs) across 8 curated colourways (Cardinal Crimson, Kingfisher Teal, Meadow Green, Blue Jay Cobalt, Goldfinch Olive, Tanager Mulberry, Canyon Ochre, and Continental Amber) with binary-search chroma gamut mapping into sRGB. - Renders a standalone square or circle-clipped SVG (
String) ready to serve over HTTP, inline into HTML, or rasterise.
Installation
Add bird-id to your Cargo.toml:
[dependencies]
bird-id = { git = "https://github.com/OffworldNexus/bird-id", branch = "develop" }
Rust API Examples
1. One-line square or circular SVG
use bird_id::{avatar_circle_svg, avatar_svg};
fn main() {
// 512×512 full-bleed square SVG
let square_svg: String = avatar_svg("ada@lovelace.org");
assert!(square_svg.starts_with("<svg"));
// 512×512 circular-clipped SVG
let circle_svg: String = avatar_circle_svg("Ada Lovelace <ADA@lovelace.org>");
assert!(circle_svg.contains("clipPath id=\"bird-id-circle\""));
}
2. Inspecting the Avatar, model, and OKLCH palette
use bird_id::Avatar;
fn main() {
let av = Avatar::from_email("Grace Hopper <grace.hopper@navy.mil>");
println!("Normalised email: {}", av.email); // "grace.hopper@navy.mil"
println!("64-bit hash: 0x{:016x}", av.hash);
println!("Template ID: {}", av.template.id); // e.g. "12-toucan"
println!("Species: {}", av.template.species);
println!("Colourway: {}", av.palette.scheme.name());
println!("Primary hue: {:.1}°", av.palette.hue);
// Every semantic token resolved for the SVG template
for (token, hex) in av.palette.tokens() {
println!(" {token:<8} -> {hex}");
}
}
3. Custom pixel sizes and RenderOptions
The SVG viewBox is always 0 0 512 512, while width and height can be set to any pixel dimension:
use bird_id::{Avatar, RenderOptions, Shape};
fn main() {
let av = Avatar::from_email("claude.shannon@bell-labs.com");
// Shorthand helpers
let square_128 = av.svg_sized(128);
let circle_64 = av.svg_circle_sized(64);
// Or via explicit RenderOptions
let custom = av.render(RenderOptions {
size: 256,
shape: Shape::Circle,
});
assert!(square_128.contains("width=\"128\" height=\"128\""));
assert!(circle_64.contains("width=\"64\" height=\"64\""));
assert!(custom.contains("width=\"256\" height=\"256\""));
}
4. Selecting or iterating over specific bird models
All 20 embedded templates are exposed via bird_id::TEMPLATES and bird_id::template::by_name:
use bird_id::{template, Avatar, TEMPLATES};
fn main() {
// Force a specific model index (0..20) while keeping the email's palette
let owl = Avatar::with_template("edsger.dijkstra@utexas.edu", 14);
assert_eq!(owl.template.species, "owl");
// Lookup a template by species name or slug
let hoopoe = template::by_name("hoopoe").expect("known species");
assert_eq!(hoopoe.id, "11-hoopoe");
// Render all 20 species using a single user's palette
for tpl in &TEMPLATES {
let svg = Avatar::with_template("collector@example.com", tpl.index).svg_circle();
assert!(!svg.is_empty());
}
}
5. Direct OKLCH colour & palette generation
bird-id exposes its perceptual colour maths (Oklch) and Palette::from_seed directly:
use bird_id::{Oklch, Palette};
fn main() {
// Gamut-map an out-of-gamut OKLCH colour into sRGB by binary-searching chroma
let colour = Oklch::new(0.72, 0.25, 45.0).gamut_mapped();
assert!(colour.in_gamut());
println!("Hex: {}", colour.to_hex());
// Build a deterministic palette from any u64 seed
let palette = Palette::from_seed(0xCAFE_BABE_DEAD_BEEF);
println!("Highlight warm0: {}", palette.warm[0]);
println!("Accent 0: {}", palette.accent[0]);
}
6. Serving avatars from an HTTP handler (e.g. axum)
use axum::{
extract::{Path, Query},
http::{header, StatusCode},
response::IntoResponse,
};
use bird_id::{Avatar, RenderOptions, Shape};
use serde::Deserialize;
#[derive(Deserialize)]
pub struct AvatarParams {
#[serde(default = "default_size")]
size: u32,
#[serde(default)]
circle: bool,
}
fn default_size() -> u32 {
256
}
pub async fn avatar_handler(
Path(email): Path<String>,
Query(params): Query<AvatarParams>,
) -> impl IntoResponse {
let svg = Avatar::from_email(&email).render(RenderOptions {
size: params.size.clamp(16, 2048),
shape: if params.circle { Shape::Circle } else { Shape::Square },
});
(
StatusCode::OK,
[
(header::CONTENT_TYPE, "image/svg+xml; charset=utf-8"),
(header::CACHE_CONTROL, "public, max-age=31536000, immutable"),
],
svg,
)
}
Command-Line Interface (birdid)
# Print a 512×512 square SVG to stdout
cargo run -- ada@lovelace.org
# Write a 256×256 circle-clipped SVG to a file
cargo run -- ada@lovelace.org --circle --size 256 -o avatar.svg
# Force a specific bird model by species name, slug, or 1-based number
cargo run -- ada@lovelace.org --model owl --circle -o owl.svg
# Inspect the normalised email, 64-bit hash, model, and OKLCH palette tokens
cargo run -- ada@lovelace.org --palette
# Render all 20 models into a directory
cargo run -- --all ./avatars --circle --size 256
The 20 Bird Models
| # | Slug | Species | Silhouette Character |
|---|---|---|---|
| 01 | 01-finch |
finch |
Round head, olive cap, two-tone seed-cracker cone beak |
| 02 | 02-cardinal |
cardinal |
Tall three-spike crest, angular mask, stout conical beak |
| 03 | 03-robin |
robin |
Domed crown, slim straight beak, teardrop throat |
| 04 | 04-goldfinch |
goldfinch |
Faceted head, top tuft, two-tone conical beak |
| 05 | 05-warbler |
warbler |
High rounded crown, horizontal eye-stripe, needle beak |
| 06 | 06-tanager |
tanager |
Angular notched crown, heavy two-tone triangular beak |
| 07 | 07-oriole |
oriole |
Faceted nape, long straight dagger beak |
| 08 | 08-chickadee |
chickadee |
Double back-tuft, bold black cap/mask, tiny stub beak |
| 09 | 09-jay |
jay |
Swept back-crest, angular cheek facets, hooked beak |
| 10 | 10-kingfisher |
kingfisher |
Compact low-slung head, oversized straight spear beak |
| 11 | 11-hoopoe |
hoopoe |
Eight-blade fan crest, long down-curved sickle beak |
| 12 | 12-toucan |
toucan |
Polygonal head, oversized four-band boat beak |
| 13 | 13-parrot |
parrot |
Swept crown feathers, deep curved hook beak |
| 14 | 14-macaw |
macaw |
Layered nape plumes, striped eye patch, heavy black hook beak |
| 15 | 15-owl |
owl |
Ear tuft, diagonal brow, concentric round facial disc & iris |
| 16 | 16-hummingbird |
hummingbird |
Angular high crown, white brow slash, long downward needle beak |
| 17 | 17-sparrow |
sparrow |
Round domed head, stout two-tone seed beak, chin bib |
| 18 | 18-waxwing |
waxwing |
Swept double crest, sleek mask, short notched beak |
| 19 | 19-puffin |
puffin |
Domed head, triangular eye mask, tall ribbed seabird beak |
| 20 | 20-quail |
quail |
Forward-curling teardrop topknot plume, bandit eye-bar |
License
Licensed under the Apache License, Version 2.0.
