feat: document the cutouts, line face and patchwork styles

This commit is contained in:
Florian Körner
2026-08-16 15:33:44 +02:00
parent 103d82d50b
commit e728fb6626
10 changed files with 192 additions and 21 deletions
+2 -2
View File
@@ -9,7 +9,7 @@
[![license](https://www.dicebear.com/badges/license.svg)](./LICENSE)
DiceBear is an open source avatar library. It turns any seed string (a username
or an email address, for example) into an SVG avatar in one of 52 styles, from
or an email address, for example) into an SVG avatar in one of 55 styles, from
hand-drawn characters to abstract patterns. The same seed always produces the
same avatar, so you store a string instead of an image and never ask users to
upload a profile picture.
@@ -54,7 +54,7 @@ avatar.toString(); // SVG string
avatar.toDataUri(); // data:image/svg+xml;charset=utf-8,...
```
The 52 avatar styles are plain JSON definitions from the
The 55 avatar styles are plain JSON definitions from the
[`dicebear/styles`](https://github.com/dicebear/styles) repository, available as
a package for each language. You can also
[create your own style](https://www.dicebear.com/guides/create-an-avatar-style-with-figma/),
@@ -11,12 +11,15 @@ const NEW_STYLES = new Set<string>([
'clay',
'constellation',
'critters',
'cutouts',
'disco',
'glyphs',
'initial-face',
'landscape',
'line-face',
'loops',
'moods',
'patchwork',
'pixelbot',
'planets',
'shape-grid',
+3 -3
View File
@@ -87,7 +87,7 @@ const RULE_Y = CREDIT_BASELINE - 40;
/**
* What shows through a transparent avatar. No avatar is ever given a
* `backgroundColor`: 35 of the 52 styles declare their own `colors.background`
* `backgroundColor`: 38 of the 55 styles declare their own `colors.background`
* palette (every `-neutral` variant, every v10 addition except glyphs, and a
* handful of older ones), and overriding it would replace a color the artist
* chose. The other 17 render transparent and sit on this.
@@ -277,7 +277,7 @@ async function renderTile(styleName: string, seed: string): Promise<string> {
* Renders the five tiles a style's card carries.
*
* Which seeds those are is decided ahead of time by
* scripts/generate-preview-seeds.mjs, which searches each style's palette for a
* scripts/generate-preview-seeds.ts, which searches each style's palette for a
* row whose tiles stay distinct in both artwork and color. Picking here instead
* meant filtering candidates on a fingerprint with color stripped, which let
* five differently drawn robots through in five shades of the same gold.
@@ -288,7 +288,7 @@ async function renderTiles(styleName: string): Promise<string[]> {
if (seeds.length !== TILE_COUNT) {
throw new Error(
`Style "${styleName}" has ${seeds.length} social card seeds, expected ${TILE_COUNT}. ` +
'OG_TILES in scripts/generate-preview-seeds.mjs and TILE_COUNT here have to agree.',
'OG_TILES in scripts/generate-preview-seeds.ts and TILE_COUNT here have to agree.',
);
}
@@ -1,4 +1,4 @@
// Generated by scripts/generate-preview-seeds.mjs. Do not edit by hand.
// Generated by scripts/generate-preview-seeds.ts. Do not edit by hand.
//
// Three rows per avatar style, each searched against that style's own palette so
// no two avatars in a row look alike: the eight a style page opens with, the
@@ -8,8 +8,8 @@
// twice. Re-run the script after upgrading @dicebear/styles or adding a style.
export const previewRowSeeds: Record<string, string[]> = {
'adventurer-neutral': ['Dax', 'Izumi', 'Celia', 'Edgar', 'Boris', 'Erik', 'Amara', 'Rocco'],
adventurer: ['Daphne', 'Imani', 'Cai', 'Emil', 'Berta', 'Ethan', 'Adele', 'Regina'],
'adventurer-neutral': ['Dax', 'Izumi', 'Celia', 'Elio', 'Bo', 'Erik', 'Asher', 'Rocco'],
adventurer: ['Davi', 'Ingrid', 'Carla', 'Evren', 'Bram', 'Emil', 'Adil', 'Roshan'],
'avataaars-neutral': ['Dax', 'Ida', 'Chiara', 'Elin', 'Beau', 'Enid', 'Anika', 'Rafael'],
avataaars: ['Dalia', 'Ina', 'Carmen', 'Ella', 'Byron', 'Ethan', 'Alia', 'Renzo'],
'big-ears-neutral': ['Dante', 'Ines', 'Casper', 'Emery', 'Bianca', 'Elise', 'Agnes', 'Rowan'],
@@ -23,6 +23,7 @@ export const previewRowSeeds: Record<string, string[]> = {
critters: ['Dina', 'Inaya', 'Cato', 'Efe', 'Boris', 'Evren', 'Amir', 'Ruby'],
'croodles-neutral': ['Dante', 'Inaya', 'Cai', 'Eden', 'Basil', 'Efe', 'Adele', 'Raul'],
croodles: ['Dana', 'Inaya', 'Cato', 'Efe', 'Baris', 'Erik', 'Anja', 'Ravi'],
cutouts: ['Dita', 'Imani', 'Ciro', 'Elias', 'Basil', 'Ezra', 'Aiden', 'Rocco'],
disco: ['Dana', 'Isaac', 'Calla', 'Ezra', 'Bea', 'Edgar', 'Adele', 'Rida'],
dylan: ['Dante', 'Ilaria', 'Cian', 'Esme', 'Blake', 'Elise', 'Amir', 'Roman'],
'fun-emoji': ['Dahlia', 'Indira', 'Cian', 'Efe', 'Bianca', 'Elise', 'Amir', 'Rania'],
@@ -33,6 +34,7 @@ export const previewRowSeeds: Record<string, string[]> = {
'initial-face': ['Dahlia', 'Ida', 'Caleb', 'Elise', 'Bilal', 'Ezra', 'Arden', 'Raul'],
initials: ['Dahlia', 'Idris', 'Cato', 'Edda', 'Basil', 'Erik', 'Adele', 'Ruby'],
landscape: ['Duncan', 'Isaac', 'Cedric', 'Emil', 'Bram', 'Eden', 'Ava', 'Rocco'],
'line-face': ['Dax', 'Imani', 'Ciro', 'Elsa', 'Bodhi', 'Edgar', 'Amir', 'Rosa'],
loops: ['Dante', 'Imani', 'Callum', 'Elio', 'Basil', 'Emil', 'Adil', 'Renzo'],
'lorelei-neutral': ['Dina', 'Isaac', 'Cedric', 'Elio', 'Bo', 'Erik', 'Aden', 'Rocco'],
lorelei: ['Dara', 'Ilse', 'Caspar', 'Erik', 'Berta', 'Emil', 'Alia', 'Regina'],
@@ -42,6 +44,7 @@ export const previewRowSeeds: Record<string, string[]> = {
'notionists-neutral': ['Dahlia', 'Ilse', 'Casper', 'Emil', 'Boris', 'Ezra', 'Anja', 'Rita'],
notionists: ['Duncan', 'Ingrid', 'Colin', 'Erik', 'Beau', 'Elif', 'Andre', 'Rida'],
'open-peeps': ['Dante', 'Ines', 'Caleb', 'Elias', 'Bella', 'Enid', 'Aisha', 'Ravi'],
patchwork: ['Dorian', 'Ilaria', 'Caleb', 'Ethan', 'Beau', 'Enid', 'Asher', 'Rowan'],
personas: ['Dax', 'Imani', 'Ciara', 'Elise', 'Ben', 'Eva', 'Aria', 'Robin'],
'pixel-art-neutral': ['Devon', 'Ingrid', 'Cyrus', 'Ezra', 'Bela', 'Elba', 'Anders', 'Rocco'],
'pixel-art': ['Dries', 'Ilse', 'Carla', 'Elena', 'Basil', 'Efe', 'Astrid', 'Roshan'],
@@ -63,8 +66,8 @@ export const previewRowSeeds: Record<string, string[]> = {
};
export const styleCardSeeds: Record<string, string[]> = {
'adventurer-neutral': ['Dina', 'Ingrid', 'Cecile', 'Ezra'],
adventurer: ['Dora', 'Ivy', 'Cecile', 'Elba'],
'adventurer-neutral': ['Davi', 'Iva', 'Ciro', 'Emil'],
adventurer: ['Dax', 'Imani', 'Celia', 'Ella'],
'avataaars-neutral': ['Davi', 'Idris', 'Cedric', 'Emil'],
avataaars: ['Darius', 'Imani', 'Carla', 'Emil'],
'big-ears-neutral': ['Daphne', 'Ida', 'Celia', 'Edda'],
@@ -78,6 +81,7 @@ export const styleCardSeeds: Record<string, string[]> = {
critters: ['Dalia', 'Ida', 'Cosima', 'Elio'],
'croodles-neutral': ['Delia', 'Ilan', 'Caleb', 'Edith'],
croodles: ['Doria', 'Idris', 'Calla', 'Elena'],
cutouts: ['Davi', 'Ismael', 'Curtis', 'Edda'],
disco: ['Dahlia', 'Ilya', 'Cosima', 'Elin'],
dylan: ['Dahlia', 'Inaya', 'Colin', 'Ella'],
'fun-emoji': ['Dalia', 'Ida', 'Caleb', 'Edgar'],
@@ -88,15 +92,17 @@ export const styleCardSeeds: Record<string, string[]> = {
'initial-face': ['Dana', 'Idris', 'Camila', 'Efe'],
initials: ['Dalia', 'Ida', 'Callum', 'Efe'],
landscape: ['Delia', 'Isabel', 'Camila', 'Edda'],
'line-face': ['Deniz', 'Iona', 'Celine', 'Eloise'],
loops: ['Dilara', 'Ina', 'Carla', 'Elif'],
'lorelei-neutral': ['Dara', 'Ingrid', 'Cai', 'Emil'],
lorelei: ['Dima', 'Ida', 'Celia', 'Elena'],
micah: ['Dana', 'Ines', 'Caspar', 'Evelyn'],
miniavs: ['Dax', 'Ismael', 'Clara', 'Elba'],
moods: ['Dahlia', 'Ines', 'Ciro', 'Ethan'],
'notionists-neutral': ['Dalia', 'Idris', 'Cedric', 'Elio'],
'notionists-neutral': ['Dalia', 'Iker', 'Caspar', 'Ella'],
notionists: ['Dalia', 'Irene', 'Cato', 'Emil'],
'open-peeps': ['Dries', 'Ilias', 'Cyrus', 'Ella'],
patchwork: ['Daphne', 'Iva', 'Ciro', 'Elsa'],
personas: ['Deniz', 'Izumi', 'Casper', 'Ella'],
'pixel-art-neutral': ['Dorian', 'Ida', 'Chloe', 'Esme'],
'pixel-art': ['Dahlia', 'Iva', 'Camila', 'Elise'],
@@ -118,8 +124,8 @@ export const styleCardSeeds: Record<string, string[]> = {
};
export const ogTileSeeds: Record<string, string[]> = {
'adventurer-neutral': ['Dahlia', 'Celine', 'Anja', 'Riley', 'Dora'],
adventurer: ['Izumi', 'Celia', 'Brenda', 'Ewan', 'Emery'],
'adventurer-neutral': ['Cedric', 'Andre', 'Alia', 'Brody', 'Ina'],
adventurer: ['Iva', 'Alia', 'Elio', 'Ismael', 'Cai'],
'avataaars-neutral': ['Dahlia', 'Deniz', 'Ilse', 'Carmen', 'Izumi'],
avataaars: ['Dahlia', 'Anika', 'Ronan', 'Raven', 'Cecile'],
'big-ears-neutral': ['Dahlia', 'Inaya', 'Elio', 'Ivy', 'Dima'],
@@ -133,6 +139,7 @@ export const ogTileSeeds: Record<string, string[]> = {
critters: ['Dahlia', 'Edda', 'Blake', 'Beau', 'Ismael'],
'croodles-neutral': ['Dahlia', 'Dina', 'Duncan', 'Dorian', 'Drew'],
croodles: ['Dahlia', 'Bianca', 'Deniz', 'Ivan', 'Bruno'],
cutouts: ['Blake', 'Dion', 'Caspar', 'Elio', 'Dries'],
disco: ['Dalia', 'Dion', 'Ingrid', 'Duncan', 'Izumi'],
dylan: ['Dalia', 'Eva', 'Denver', 'Casper', 'Duncan'],
'fun-emoji': ['Dax', 'Denver', 'Diana', 'Dorian', 'Duncan'],
@@ -143,15 +150,17 @@ export const ogTileSeeds: Record<string, string[]> = {
'initial-face': ['Dalia', 'Dion', 'Ilaria', 'Dora', 'Eden'],
initials: ['Dana', 'Devon', 'Diana', 'Drew', 'Inaya'],
landscape: ['Alma', 'Ruby', 'Cesar', 'Doria', 'Elba'],
'line-face': ['Boris', 'Ilaria', 'Casper', 'Rhys', 'Ida'],
loops: ['Dahlia', 'Ilan', 'Devon', 'Dion', 'Cecile'],
'lorelei-neutral': ['Iva', 'Dora', 'Elba', 'Celia', 'Dilara'],
lorelei: ['Cato', 'Dina', 'Cesar', 'Renzo', 'Rocco'],
micah: ['Dahlia', 'Elif', 'Delia', 'Robin', 'Bodil'],
miniavs: ['Ilias', 'Colin', 'Dries', 'Deniz', 'Aria'],
moods: ['Erik', 'Dilan', 'Ina', 'Emil', 'Bram'],
'notionists-neutral': ['Dana', 'Dora', 'Iva', 'Celia', 'Renzo'],
'notionists-neutral': ['Dana', 'Iva', 'Dora', 'Celia', 'Renzo'],
notionists: ['Bela', 'Dina', 'Dorian', 'Denver', 'Elio'],
'open-peeps': ['Cecile', 'Ayla', 'Chiara', 'Eva', 'Idris'],
patchwork: ['Dora', 'Agnes', 'Deniz', 'Ismael', 'Dilan'],
personas: ['Britt', 'Baxter', 'Alia', 'Cedric', 'Evren'],
'pixel-art-neutral': ['Clara', 'Idris', 'Baxter', 'Andre', 'Calla'],
'pixel-art': ['Dalia', 'Dorian', 'Ilona', 'Dilan', 'Colin'],
@@ -203,7 +212,7 @@ function lookUp(
if (!seeds) {
throw new Error(
`Avatar style "${styleName}" has no ${label}. Run node scripts/generate-preview-seeds.mjs.`,
`Avatar style "${styleName}" has no ${label}. Run node scripts/generate-preview-seeds.ts.`,
);
}
@@ -33,6 +33,7 @@ const styleCategories: Record<string, StyleCategory> = {
critters: 'Characters',
croodles: 'Characters',
'croodles-neutral': 'Characters',
cutouts: 'Characters',
disco: 'Minimalist',
dylan: 'Characters',
'fun-emoji': 'Characters',
@@ -43,6 +44,7 @@ const styleCategories: Record<string, StyleCategory> = {
'initial-face': 'Minimalist',
initials: 'Minimalist',
landscape: 'Scenes',
'line-face': 'Characters',
loops: 'Minimalist',
lorelei: 'Characters',
'lorelei-neutral': 'Characters',
@@ -52,6 +54,7 @@ const styleCategories: Record<string, StyleCategory> = {
notionists: 'Characters',
'notionists-neutral': 'Characters',
'open-peeps': 'Characters',
patchwork: 'Minimalist',
personas: 'Characters',
'pixel-art': 'Characters',
'pixel-art-neutral': 'Characters',
@@ -287,6 +287,7 @@ h6,
'critters',
'croodles',
'croodles-neutral',
'cutouts',
'disco',
'dylan',
'fun-emoji',
@@ -297,6 +298,7 @@ h6,
'initial-face',
'initials',
'landscape',
'line-face',
'loops',
'lorelei',
'lorelei-neutral',
@@ -306,6 +308,7 @@ h6,
'notionists',
'notionists-neutral',
'open-peeps',
'patchwork',
'personas',
'pixel-art',
'pixel-art-neutral',
@@ -22,6 +22,7 @@ const definitionImports: Record<string, () => Promise<{ default: unknown }>> = {
critters: () => import('@dicebear/styles/critters.json'),
croodles: () => import('@dicebear/styles/croodles.json'),
'croodles-neutral': () => import('@dicebear/styles/croodles-neutral.json'),
cutouts: () => import('@dicebear/styles/cutouts.json'),
disco: () => import('@dicebear/styles/disco.json'),
dylan: () => import('@dicebear/styles/dylan.json'),
'fun-emoji': () => import('@dicebear/styles/fun-emoji.json'),
@@ -32,6 +33,7 @@ const definitionImports: Record<string, () => Promise<{ default: unknown }>> = {
'initial-face': () => import('@dicebear/styles/initial-face.json'),
initials: () => import('@dicebear/styles/initials.json'),
landscape: () => import('@dicebear/styles/landscape.json'),
'line-face': () => import('@dicebear/styles/line-face.json'),
loops: () => import('@dicebear/styles/loops.json'),
lorelei: () => import('@dicebear/styles/lorelei.json'),
'lorelei-neutral': () => import('@dicebear/styles/lorelei-neutral.json'),
@@ -42,6 +44,7 @@ const definitionImports: Record<string, () => Promise<{ default: unknown }>> = {
'notionists-neutral': () =>
import('@dicebear/styles/notionists-neutral.json'),
'open-peeps': () => import('@dicebear/styles/open-peeps.json'),
patchwork: () => import('@dicebear/styles/patchwork.json'),
personas: () => import('@dicebear/styles/personas.json'),
'pixel-art': () => import('@dicebear/styles/pixel-art.json'),
'pixel-art-neutral': () => import('@dicebear/styles/pixel-art-neutral.json'),
@@ -122,6 +125,8 @@ export async function loadAvatarStyleDefinition(
return raw as StyleDefinition;
}
const pendingStyles = new Map<string, Promise<Style>>();
export async function loadAvatarStyle(avatarStyle: string): Promise<Style> {
const cached = styleCache.get(avatarStyle);
@@ -160,16 +165,35 @@ export async function loadAvatarStyle(avatarStyle: string): Promise<Style> {
throw new Error(`Avatar style "${avatarStyle}" not found.`);
}
const def = await loader();
const raw = def.default as object;
// A grid of avatars asks for the same style once per tile, all in the same
// tick and all before the first import resolves. Sharing the in-flight
// promise keeps that to one `new Style()`, which costs a few milliseconds
// per call on the larger definitions.
const inFlight = pendingStyles.get(name);
definitionRawCache.set(name, raw);
if (inFlight) {
return inFlight;
}
const style = new Style(raw);
const promise = loader()
.then((def) => {
const raw = def.default as object;
styleCache.set(name, style);
definitionRawCache.set(name, raw);
return style;
const style = new Style(raw);
styleCache.set(name, style);
return style;
})
.finally(() => {
pendingStyles.delete(name);
});
pendingStyles.set(name, promise);
return promise;
}
function scanForVariable(obj: unknown, variableName: string): boolean {
+43
View File
@@ -0,0 +1,43 @@
---
title: Cutouts Avatar Style
description: >
Create Cutouts avatars with the DiceBear avatar library. Generate unique
profile pictures using our free avatar API or JavaScript avatar library.
outline: [2, 3]
---
<script setup lang="ts">
import StylePreview from "@theme/components/styles/StylePreview.vue";
import StyleInfo from "@theme/components/styles/StyleInfo.vue";
import StyleDescription from "@theme/components/styles/StyleDescription.vue";
import StyleUsage from "@theme/components/styles/StyleUsage.vue";
import StylePresets from "@theme/components/styles/StylePresets.vue";
import StyleOptions from "@theme/components/styles/StyleOptions.vue";
</script>
# Cutouts
Cutouts is a paper collage avatar style. Every face is assembled from torn craft
paper: a colored head, hair laid over it, and eyes, nose and mouth cut out
separately. The two eyes never match, which is the point of the style. Generate
warm, handmade profile pictures for communities and editorial sites.
<StylePreview styleName="cutouts" />
<StyleDescription styleName="cutouts" />
## Usage
<StyleUsage styleName="cutouts" />
## Presets
<StylePresets styleName="cutouts" :limit="5" />
## Options
<StyleOptions styleName="cutouts" />
## Details
<StyleInfo styleName="cutouts" />
+43
View File
@@ -0,0 +1,43 @@
---
title: Line Face Avatar Style
description: >
Create Line Face avatars with the DiceBear avatar library. Generate unique
profile pictures using our free avatar API or JavaScript avatar library.
outline: [2, 3]
---
<script setup lang="ts">
import StylePreview from "@theme/components/styles/StylePreview.vue";
import StyleInfo from "@theme/components/styles/StyleInfo.vue";
import StyleDescription from "@theme/components/styles/StyleDescription.vue";
import StyleUsage from "@theme/components/styles/StyleUsage.vue";
import StylePresets from "@theme/components/styles/StylePresets.vue";
import StyleOptions from "@theme/components/styles/StyleOptions.vue";
</script>
# Line Face
Line Face draws a face with a few brush strokes and leaves out everything else.
Eight eye pairs, six noses and eight mouths, each a single stroke, sit directly
on a warm background with no head outline around them. Generate quiet, minimal
profile icons for reading apps and personal sites.
<StylePreview styleName="line-face" />
<StyleDescription styleName="line-face" />
## Usage
<StyleUsage styleName="line-face" />
## Presets
<StylePresets styleName="line-face" :limit="5" />
## Options
<StyleOptions styleName="line-face" />
## Details
<StyleInfo styleName="line-face" />
+43
View File
@@ -0,0 +1,43 @@
---
title: Patchwork Avatar Style
description: >
Create Patchwork avatars with the DiceBear avatar library. Generate unique
profile pictures using our free avatar API or JavaScript avatar library.
outline: [2, 3]
---
<script setup lang="ts">
import StylePreview from "@theme/components/styles/StylePreview.vue";
import StyleInfo from "@theme/components/styles/StyleInfo.vue";
import StyleDescription from "@theme/components/styles/StyleDescription.vue";
import StyleUsage from "@theme/components/styles/StyleUsage.vue";
import StylePresets from "@theme/components/styles/StylePresets.vue";
import StyleOptions from "@theme/components/styles/StyleOptions.vue";
</script>
# Patchwork
Patchwork lays a quilt from traditional blocks such as pinwheel, flying geese
and rail fence. Each avatar picks two of the eighteen blocks and repeats them as
diagonal twins rotated by 180 degrees, so every quilt comes out point symmetric.
Generate textile abstract avatars for user accounts and placeholders.
<StylePreview styleName="patchwork" />
<StyleDescription styleName="patchwork" />
## Usage
<StyleUsage styleName="patchwork" />
## Presets
<StylePresets styleName="patchwork" :limit="5" />
## Options
<StyleOptions styleName="patchwork" />
## Details
<StyleInfo styleName="patchwork" />