- Rust 100%
| .cargo | ||
| src | ||
| .gitignore | ||
| Cargo.toml | ||
| LICENSE.txt | ||
| README.md | ||
Introduction
This is a command line tool that generates the same unicorn avatar images as the Unicornify web service, just in better quality. For instance, the unicorn avatar for the email address mail@example.com, when generated by the website, looks like this:
The website returns JPEGs with a maximum size of 128x128 pixels. This tool gives you any size you want, and it gives you a PNG image:
It also has a few nice extra options.
This is a Rust port of the original go-unicornify by balpha. Built with the bitcompat feature it reproduces the Go tool's output bit-for-bit: the same hash produces a pixel-identical avatar (verified across many hashes and every option, including the Python-compatible PRNG seeding, the math functions, and the renderer's tie-breaking). It can be used both as a command-line tool and as a library.
Download / Installation
You need a Rust toolchain (2024 edition). The build targets musl by default (configured in .cargo/config.toml) so the release binary is small and statically linked — add the target once:
rustup target add x86_64-unknown-linux-musl
Then build the release binary with:
cargo build --release
The executable is a self-contained, stripped static binary at target/x86_64-unknown-linux-musl/release/unicornify (≈900 KB). You can copy it anywhere on your PATH. Alternatively, run it directly through Cargo, passing arguments after --:
cargo run --release -- -m mail@example.com
The release profile is tuned for minimum binary size (opt-level = "z", fat LTO, panic = "abort", stripped); this trades away some run-time speed, which is noticeable for the CPU-bound renderer. To build a faster, native (glibc) binary instead, override the target:
cargo build --release --target x86_64-unknown-linux-gnu
Please refer to the LICENSE.txt file for redistribution information.
Build variants (feature flags)
Bit-for-bit compatibility with the original Go program (and thus the website) — the same hash yielding a pixel-identical PNG — relies on a few hand-ported pieces: Go's exact sin/cos/atan/exp/pow, its sort.Sort, and the Python-compatible MT19937 PRNG. They're behind two Cargo features (both off by default):
bitcompat: exact ports of Go's math functions and sort. Impliespyrand.pyrand(implied bybitcompat): the Python/Go-compatible MT19937 PRNG. This is what decides which unicorn a hash maps to.
Three useful builds:
cargo build --release # default: std math/sort + std PRNG
cargo build --release --features pyrand # std math/sort, MT19937 PRNG
cargo build --release --features bitcompat # bit-exact (matches Go / the website)
The default build is leaner (it doesn't compile the hand-ported modules) and still produces valid, deterministic unicorns. Compared with bitcompat: adding only pyrand differs by just a handful of pixels (the std math/sort deviate by ≤1); the plain default swaps the random sequence too, so each hash maps to a different (but still valid and deterministic) unicorn.
Using as a library
The crate is also a library. Add it as a dependency (enable bitcompat if you need output identical to the website/Go):
[dependencies]
unicornify = { path = "…", features = ["bitcompat"] }
use unicornify::{generate, mail_to_hash, Options};
let hash = mail_to_hash("mail@example.com");
let opts = Options { size: 256, ..Default::default() };
let (image, data) = generate(&hash, &opts, None)?; // image: image::RgbaImage
image.save("unicorn.png")?;
println!("body hue: {}", data.unicorn_data.body_hue);
generate is the easy path (handles anti-aliasing and alpha); make_avatar is the low-level renderer. Options defaults match the CLI defaults. See the crate docs (cargo doc --open) for the full API.
Usage
A note on flags: short, single-letter options keep their single dash (
-m,-h,-r,-s,-o,-f,-z), while the multi-character options use a double dash, as is conventional for Rust/Clap programs:--noaa,--noshading,--nograss,--serial,--dataout, and--svg. (Apart from--svg, which is new in this port, the original Go tool used a single dash for those too.)
Specifying the unicorn
Unicorns are always generated based on a single (hexadecimal) number. Usually, this number is generated from an email address (by taking the MD5 hash, just like Gravatar). For example, the number corresponding to the address mail@example.com is 7daf6c79d4802916d83f6266e24850af.
You have three options how to tell the program which Unicorn to render: With the -m, -h, and -r options.
unicornify -m mail@example.com
unicornify -h 7daf6c79d4802916d83f6266e24850af
unicornify -r
With -m you pass the email address you want to generate the avatar for, and the program will generate the hash number.
With -h, you pass the number directly. Although both -m and -r always generate 32-digit numbers, this is not a requirement. You can pass hex numbers of any length to -h.
Finally with -r, you tell program to generate a random number, and thus a random unicorn.
You must specify one (exactly one) of the above three. All the other following settings are optional.
Output file
By default, the unicorn image is saved to a file called {hexnumber}.png, where {hexnumber} is the number the unicorn is generated from. To specify a different file name, use the -o switch:
unicornify -r -o awesome-unicorn.png
Image size
The generated images are always square. You can specify the width (and thus height) you want with the -s switch:
unicornify -m mail@example.com -s 1024
will generate a 1024x1024 pixel image.
Shading & grass
By default, unicorns are generated with some amount of shading, and with grass on the ground. If you want the "classic" (legacy) style images that look fairly flat, you can use the --noshading and --nograss parameters. Compare:
Those two images were generated with these commands:
unicornify -h b50eb7b293596008ecbb108815f82d31 -s 400
unicornify -h b50eb7b293596008ecbb108815f82d31 -s 400 --noshading --nograss
Transparent background
Unicorns want to be free! To render the unicorn without the background, e.g. for inserting it into another image, use the -f switch.
unicornify -m mail@example.com -f
Zoom out
On some avatars, the unicorn is fully visible, on others, only the head may be shown. If your unicorn is not fully visible, but you need it in full (to print it on a T-Shirt maybe?), you can use the -z switch.
unicornify -m mail@example.com -z
You'll usually want to combine this with -f.
The following images show the effects of the -f and -z switches:
The three pictures were generated, in order, with the following commands:
unicornify -o example.png -h 772b4415cd38c3af6b522087a6359194 -s 200
unicornify -o example.png -h 772b4415cd38c3af6b522087a6359194 -s 200 -f
unicornify -o example.png -h 772b4415cd38c3af6b522087a6359194 -s 200 -f -z
Disable anti-aliasing
The drawing algorithm produces very aliased images. In order to create smoother lines, the program behind the scenes creates an image that's twice as wide and twice as high as the requested size, and then uses bilinear interpolation to downscale the image. If, for whatever reason, you want to disable this anti-aliasing, you can use the --noaa switch.
This picture shows a detail of the above image, magnified four-fold:
The left image was generated normally, the right image with disabled anti-aliasing.
Disable parallelization
The drawing operation is parallelized by default to make use of multiple processor cores. You can disable this with the --serial switch.
Save avatar data to a JSON file
To save all the data (colors, angles, sizes etc.) to a JSON file, pass --dataout filename.json.
Output as a scalable SVG
Instead of a raster PNG you can emit a resolution-independent SVG vector image with --svg:
unicornify -m mail@example.com --svg
This writes {hash}.svg (or the -o filename) and skips the PNG. The SVG is a vector approximation of the rendered avatar rather than a pixel-for-pixel copy. It draws the sky/ground/rainbow/clouds background by default (pass -f for a transparent background), but omits the grass and cast shadows, and fakes the 3D lighting with gradients (pass --noshading for a flat look). Because the unicorn is built from overlapping 3D solids whose visibility is normally resolved per pixel, the SVG paints its shapes back-to-front, so deeply interpenetrating parts can differ slightly from the PNG. -s sets the canvas size — though the SVG scales to any size regardless — and -z (zoom out) works as usual. The same hash always maps to the same unicorn as the PNG.
The library exposes this as generate_svg (and the lower-level make_svg), mirroring generate/make_avatar but returning the SVG document as a String.






