gsichtl 0.4.0

Deterministic pixel-art avatars from any seed: monsters, nerdy faces, crests, badges, realms and retro sprites
Documentation

gsichtl

Deterministic pixel-art avatars from any seed. Gsichtl is Bavarian for "little face".

32 generated faces

16 generated crests, 16 generated badges, 16 generated realms and 16 generated retro sprites

The seed is any byte string, typically a public user or group id. The same seed always gives the same picture, so every client draws it locally and nothing has to be stored or sent. The crate does no I/O, is #![forbid(unsafe_code)] and has one dependency, blake3, plus png behind an optional feature.

Generators

  • gsichtl::monster(seed): an 8x8 monster with antennae, horns or ears, big eyes and a mouth. Up to 324,000 monsters: 4 backgrounds, 10 bodies, 6 accents, 6 tops, 3 silhouettes, 3 brows, 5 eyes and 5 mouths.
  • gsichtl::nerd(seed): an 8x8 human face with hair, glasses or goggles and sometimes headphones. Up to 1,327,104 nerds: 4 backgrounds, 6 skins, 8 hair colors, 3 frames, 6 accents, 2 heads, 6 hair styles, 4 eyes, 4 mouths and with or without headphones.
  • gsichtl::face(seed): a monster or a nerd, chosen by the seed (about half each). It is not necessarily monster(seed) or nerd(seed).
  • gsichtl::crest(seed): a 10x10 shield with a division and an emblem, for groups. 28,800 crests: 4 backgrounds, 6 fields, 5 second fields, 2 metals, 3 shields, 5 divisions and 8 emblems.
  • gsichtl::badge(seed): a 12x12 round badge (disc, octagon or cog) with a division, an emblem and sometimes a metal rim, for groups. 57,600 badges: 4 backgrounds, 6 fields, 5 second fields, 2 metals, 3 shapes, with or without a rim, 5 divisions and 8 emblems.
  • gsichtl::realm(seed): a 20x20 small world for groups: a floating island, a planet or a floating patch of sea, with a landmark, a sky detail and sometimes a waterfall or a ring. Made to stand free, rendered with to_rgba_transparent or to_png_transparent. 34,560 realms: 4 backgrounds, 3 shapes (an island counts twice in the draw), 6 biomes, 5 landmarks per shape, 6 accents, with or without a waterfall or ring, and 4 sky details.
  • gsichtl::retro(seed): a 20x20 retro game sprite for groups: a creature, a weapon, an item or a piece of old tech, palette-swapped in neon colors, sometimes in front of an explosion, a blood splatter or sparkles. Original designs in the style of classic games. Made to stand free like a realm. 21,504 retros: 4 backgrounds, 24 motifs, 8 colors, 7 second colors and 4 effects.
  • gsichtl::icon(seed): a realm or a retro sprite, chosen by the seed (about half each), the group counterpart of face. It is not necessarily realm(seed) or retro(seed).

The face counts are upper bounds: where no part uses the accent color, two faces that differ only in their accent look the same.

Installation

[dependencies]
gsichtl = "0.1"

For PNG output, turn on the png feature:

[dependencies]
gsichtl = { version = "0.1", features = ["png"] }

Usage

Render to RGBA8:

let avatar = gsichtl::face(b"alice");
// 12 px per cell, 16 px of background around the grid: 128 x 128 RGBA8.
let image = avatar.to_rgba(12, 16);
assert_eq!((image.width, image.height), (128, 128));

Draw the cells yourself:

let a = gsichtl::crest(b"community-7");
for y in 0..a.side() {
    for x in 0..a.side() {
        let color = a.cell(x, y).unwrap_or(a.background());
        // Fill the square at (x, y) with `color`, an sRGB `[u8; 3]`.
    }
}

Encode as PNG, with the png feature:

let png = gsichtl::face(b"alice").to_png(16, 8)?;
std::fs::write("alice.png", png)?;

An image is side * cell_px + 2 * margin_px pixels wide and high, at most 3060. cell_px == 0 gives a plain square of background.

to_rgba and to_png are opaque. to_rgba_transparent and to_png_transparent render the same picture, but leave the background and the margin fully transparent, so the avatar stands free on your own surface:

let image = gsichtl::crest(b"community-7").to_rgba_transparent(3, 2);
assert_eq!(&image.pixels[..4], &[0, 0, 0, 0]);

How it works

The seed goes through BLAKE3 in derive-key mode, with one context string per generator: gsichtl 2026-10-02 face v1, gsichtl 2026-10-02 monster v1, gsichtl 2026-10-02 nerd v1, gsichtl 2026-10-02 crest v1, gsichtl 2026-10-03 badge v1, gsichtl 2026-10-03 realm v1, gsichtl 2026-10-03 retro v1 and gsichtl 2026-10-03 icon v1. The extendable output is the stream of draws: each draw takes 4 bytes, modulo the number of choices. The parts are small character grids, painted in a fixed order. Eyes, brows and mouths are clipped to the painted body, so they never leave the silhouette.

BLAKE3 serves as a deterministic mixer here, not for security. Its output is fixed by its specification, so updating the blake3 crate does not change a picture.

Sample sheets

cargo run --example render --features png -- <dir> [count]

writes face-NN.png from the seed user-N, and crest-NN.png, badge-NN.png, realm-NN.png and retro-NN.png from the seed community-N into <dir>. Faces and badges have 16 px cells and an 8 px margin. Crests have 18 px cells and a 14 px margin; realms and retro sprites have 10 px cells and a 4 px margin on a transparent background, so all of them are 208 px wide like a badge. The sheets above show 32 faces, and 16 crests above 16 badges above 16 realms above 16 retro sprites:

magick montage <dir>/face-*.png -tile 8x -geometry +4+4 -background none docs/faces.png
magick montage <dir>/crest-0?.png <dir>/crest-1[0-5].png \
    <dir>/badge-0?.png <dir>/badge-1[0-5].png \
    <dir>/realm-0?.png <dir>/realm-1[0-5].png \
    <dir>/retro-0?.png <dir>/retro-1[0-5].png \
    -tile 8x -geometry +4+4 -background none docs/groups.png

Stability

The same seed gives the same avatar within a 0.x minor version. Changing a part, a palette, the draw order or a context string is a breaking change and bumps the minor version, because everyone who draws an avatar for the same id has to see the same picture. tests/gsichtl.rs pins fingerprints of known seeds.

Not an identity check

Anyone can upload a picture that looks like another person's generated face. A generated avatar is a decoration; never present it as verification of who someone is.

Minimum Rust version

Rust 1.91, edition 2024.

License

MIT, see LICENSE.