Meziantou.Framework.Avatar
2.1.9
Prefix Reserved
dotnet add package Meziantou.Framework.Avatar --version 2.1.9
NuGet\Install-Package Meziantou.Framework.Avatar -Version 2.1.9
<PackageReference Include="Meziantou.Framework.Avatar" Version="2.1.9" />
<PackageVersion Include="Meziantou.Framework.Avatar" Version="2.1.9" />
<PackageReference Include="Meziantou.Framework.Avatar" />
paket add Meziantou.Framework.Avatar --version 2.1.9
#r "nuget: Meziantou.Framework.Avatar, 2.1.9"
#:package Meziantou.Framework.Avatar@2.1.9
#addin nuget:?package=Meziantou.Framework.Avatar&version=2.1.9
#tool nuget:?package=Meziantou.Framework.Avatar&version=2.1.9
Meziantou.Framework.Avatar
Generate deterministic avatars from a name, as SVG strings or as vector scenes to draw as PNG images.
Usage
The types are in the Meziantou.Framework namespace.
var svg = AvatarGenerator.CreateSvg("John Doe");
Assert.StartsWith("<svg ", svg);
Assert.Contains("aria-label=\"JD\"", svg);
AvatarGenerator extracts a 1-2 grapheme bigram from the name and selects a background/foreground pair from the palette using a hash of the name.
Customize the output
var options = new AvatarOptions
{
Bigram = "JD", // optional explicit 1-2 grapheme bigram
Shape = AvatarShape.Round, // Round, Square, or RoundedSquare
Size = 128,
};
options.Palette.Clear();
options.Palette.Add(new AvatarColorPair("#CFDADE", "#153037")); // background, then foreground
var svg = AvatarGenerator.CreateSvg("John Doe", options);
Assert.Contains("width=\"128\" height=\"128\"", svg);
Assert.Contains("<circle cx=\"64\" cy=\"64\" r=\"64\" fill=\"#CFDADE\"/>", svg);
Render as PNG
AvatarGenerator.CreateScene returns the avatar as a Scene of Meziantou.Framework.VectorGraphics.
Meziantou.Framework.Imaging.VectorGraphics draws it as an image:
// Lay the avatar out with the fonts that draw the image, so the initials are centered
var measurer = SceneImageRenderer.CreateMeasurer();
var scene = AvatarGenerator.CreateScene("John Doe", new AvatarOptions { Size = 128 }, measurer);
using var image = scene.ToImage();
Assert.Equal((128, 128), (image.Width, image.Height));
// Save to a file (image.Save("avatar.png")) or to a stream, in any format
using var png = new MemoryStream();
image.Save(png, new PngEncoder());
The initials are centered with the metrics of the measurer, so lay the scene out with the measurer of the fonts that draw it. The embedded fonts only cover the Latin characters: for other scripts, pass a SceneFontCollection with fallback fonts in SceneImageOptions.Fonts to CreateMeasurer. The scene keeps the fonts of the measurer (Scene.Fonts), so ToImage draws the initials with them, and SvgRenderOptions.EmbedFonts embeds them in its SVG. A scene only supports the colors that SceneColor.Parse accepts (#rrggbb, rgb(), hsl(), CSS color names), not var(--x) or currentColor.
How the bigram is chosen
The name is split on whitespace and on the connectors used inside compound names (-, ', ., _, and their typographic variants):
- Two or more words: the first grapheme of the first word plus the first grapheme of the last word.
"John Michael Doe"givesJD, notJM."Jean-Pierre"givesJPand"O'Brien"givesOB. - A single word: its first two graphemes.
"John"givesJoand"山田太郎"gives山田.
The unit is a grapheme cluster, not a char, so an emoji or a decomposed accent counts as one. Characters that render nothing (zero-width, bidi controls, standalone combining marks) are skipped, and a name with nothing visible renders ?.
Set AvatarOptions.Bigram to bypass this entirely. It does not affect the color, which always derives from the name.
Accessibility
By default the avatar is exposed as role="img" with the bigram as its aria-label. Two options change this:
AccessibleLabelsets the announced text — pass the full name so it announces as "John Doe" rather than "JD".IsDecorativehides the avatar from assistive technologies witharia-hidden. Use it when the avatar sits next to the name it represents, so the name is not announced twice.
Determinism
The color is hash(name) % Palette.Count, so the same name always maps to the same pair — for a fixed palette and a fixed package version. Two consequences are worth knowing before caching or persisting generated avatars:
- The number of entries in the palette and their order are part of the mapping. Adding or removing a single
AvatarColorPairre-colors nearly every name, not just the names that mapped to the changed entry. - The name is trimmed and normalized to Unicode Form C before hashing, so surrounding whitespace is ignored but case is not:
"john doe"and"John Doe"get different colors.
Normalization requires ICU. Under InvariantGlobalization it is a no-op, so a name that is not already in Form C selects a different palette entry there than it does in a normal deployment. If you render avatars from both kinds of process and need them to agree, normalize the name yourself before calling CreateSvg.
Input handling
The generated string is always well-formed XML. Characters that the XML 1.0 specification forbids — control characters, unpaired surrogates from a name truncated mid-emoji — are removed from name and from AccessibleLabel, so an untrusted display name can be passed straight through. An explicit Bigram containing such a character is rejected with an ArgumentException instead, since that indicates a bug in the calling code.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. net11.0 is compatible. |
-
net10.0
- Meziantou.Framework.VectorGraphics (>= 1.0.3)
- System.IO.Hashing (>= 10.0.14)
-
net11.0
- Meziantou.Framework.VectorGraphics (>= 1.0.3)
- System.IO.Hashing (>= 10.0.14)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.1.9 | 0 | 11/15/2026 |
| 2.1.8 | 43 | 11/14/2026 |
| 2.1.7 | 45 | 11/13/2026 |
| 2.1.6 | 95 | 11/8/2026 |
| 2.1.5 | 167 | 10/28/2026 |
| 2.1.4 | 95 | 10/21/2026 |
| 2.1.3 | 97 | 10/16/2026 |
| 2.1.2 | 130 | 9/11/2026 |
| 2.1.1 | 129 | 9/6/2026 |
| 2.1.0 | 128 | 9/4/2026 |
| 2.0.2 | 131 | 8/29/2026 |
| 2.0.1 | 134 | 8/9/2026 |
| 2.0.0 | 152 | 7/5/2026 |
| 1.0.6 | 139 | 6/28/2026 |
| 1.0.5 | 153 | 6/13/2026 |
| 1.0.4 | 147 | 6/7/2026 |
| 1.0.3 | 137 | 5/23/2026 |
| 1.0.2 | 139 | 5/14/2026 |
| 1.0.1 | 140 | 4/25/2026 |
| 1.0.0 | 148 | 4/6/2026 |