@maxnth/gestalt
@maxnth/gestalt
l
Give @maxnth/gestalt a string or number — a user id, email, handle, or anything else — and it renders a unique inline SVG avatar. The same seed always produces the same result, so you get consistent, good-looking avatars entirely from data.
Why use it?
- No assets to ship. Every avatar is generated on the fly, so there is no image folder, no CDN, and no broken links.
- Stable by design. A seed like
ada@example.comalways produces the exact same avatar. - Server-rendered. Built-in Vue components emit complete inline SVG during SSR, with no client-side drawing pass.
- Flexible core. Use a component, generate a framework-independent SVG document, or keep using the canvas helpers for raster export and custom algorithms.
- Easy to extend. Bring your own rendering algorithm via the generic
<Avatar>component. - Fully typed. Written in TypeScript with declarations included.
Install
pnpm add @maxnth/gestalt
# or: npm i / yarn add / bun add
Requires vue >= 3.5.
Usage
<GradientAvatar>
The quickest way to start is the mesh-gradient avatar component.
<script setup>
import { GradientAvatar } from '@maxnth/gestalt'
</script>
<template>
<GradientAvatar seed="jane@example.com" :size="96" />
</template>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
seed |
string | number |
— | Any value; each unique seed yields a unique avatar. |
size |
number |
32 |
Rendered size in pixels. |
radius |
number | string |
"9999px" |
Corner radius. Number is pixels; string is CSS. |
variant |
"mesh" | "bloom" | "flare" | "silk" | "nova" | "void" | "jade" | "auto" |
"mesh" |
Gradient composition. auto selects one deterministically from the seed. |
mode |
"smooth" | "dither" |
"smooth" |
Smooth gradient or palette-quantized square cells. |
style |
Record<string, string | number> |
— | Extra inline styles merged onto the wrapper. |
class is forwarded to the wrapper <span> like any standard Vue attribute.
<!-- Palette-quantized pixel treatment -->
<GradientAvatar seed="jane@example.com" :size="96" mode="dither" />
Ring / border
All avatar components support a ring prop that draws an outline around the avatar using box-shadow, so it naturally follows the chosen radius.
<GradientAvatar seed="jane@example.com" :size="96" ring />
<GradientAvatar seed="jane@example.com" :size="96" :ring="4" />
<GradientAvatar seed="jane@example.com" :size="96" ring="0 0 0 3px #10b981" />
ring value |
Result |
|---|---|
true |
Default white ring (2px). |
number |
White ring with the given thickness in pixels. |
string |
Any CSS box-shadow value for full control. |
| omitted | No ring. |
<!-- Circle -->
<GradientAvatar seed="jane@example.com" :size="96" />
<!-- Rounded square -->
<GradientAvatar seed="jane@example.com" :size="96" :radius="16" />
<!-- Square -->
<GradientAvatar seed="jane@example.com" :size="96" :radius="0" />
<IdenticonAvatar>
For a GitHub-style geometric avatar, render a symmetric grid of cells with a deterministic color pair.
<script setup>
import { IdenticonAvatar } from '@maxnth/gestalt'
</script>
<template>
<IdenticonAvatar seed="jane@example.com" :size="96" />
<IdenticonAvatar seed="jane@example.com" :size="96" mirror="vertical" />
</template>
| Prop | Type | Default | Description |
|---|---|---|---|
seed |
string | number |
— | Any value; each unique seed yields a unique pattern. |
size |
number |
32 |
Rendered size in pixels. |
radius |
number | string |
"9999px" |
Corner radius. Number is pixels; string is CSS. |
mirror |
"horizontal" | "vertical" | "both" | "none" |
"horizontal" |
Which axis to mirror. |
foreground |
string |
— | Cell color as #RGB or #RRGGBB. Invalid values use the generated color. |
background |
string |
— | SVG background as #RGB or #RRGGBB. Invalid values use the generated color. |
style |
Record<string, string | number> |
— | Extra inline styles merged onto the wrapper. |
<FlowFieldAvatar>
For a plotter-style generative avatar, render streamlines through a deterministic noise field.
<script setup>
import { FlowFieldAvatar } from '@maxnth/gestalt'
</script>
<template>
<FlowFieldAvatar seed="jane@example.com" :size="96" />
<FlowFieldAvatar seed="jane@example.com" :size="96" :columns="9" :rows="9" :iterations="56" />
</template>
| Prop | Type | Default | Description |
|---|---|---|---|
seed |
string | number |
— | Any value; each unique seed yields a unique flow field. |
size |
number |
32 |
Rendered size in pixels. |
radius |
number | string |
"9999px" |
Corner radius. Number is pixels; string is CSS. |
columns |
number |
14 |
Number of seed columns. |
rows |
number |
14 |
Number of seed rows. |
iterations |
number |
28 |
Steps traced in each direction from every seed. |
stepSize |
number |
— | Step size in internal SVG user-space units. |
background |
string |
— | SVG background as #RGB or #RRGGBB. |
colors |
string[] |
— | #RGB or #RRGGBB stroke colors; invalid entries are ignored. |
strokeWidth |
number |
— | Stroke width in internal SVG user-space units. |
opacity |
number |
0.72 |
Stroke opacity from 0 to 1. |
style |
Record<string, string | number> |
— | Extra inline styles merged onto the wrapper. |
<InitialsAvatar>
For a more traditional placeholder avatar, render one or two initials on a solid background.
<script setup>
import { InitialsAvatar } from '@maxnth/gestalt'
</script>
<template>
<InitialsAvatar seed="Maximilian Nöth" :size="96" mode="single" />
<InitialsAvatar seed="Maximilian Nöth" :size="96" mode="double" />
</template>
Double initials split the seed by spaces, dashes, underscores, and dots — so Ada Lovelace becomes AL.
| Prop | Type | Default | Description |
|---|---|---|---|
seed |
string | number |
— | The name or value used to derive the initials. |
size |
number |
32 |
Rendered size in pixels. |
radius |
number | string |
"9999px" |
Corner radius. Number is pixels; string is CSS. |
mode |
"single" | "double" |
"single" |
Whether to show one or two initials. |
background |
string |
— | Background as #RGB or #RRGGBB; invalid values use the generated color. |
color |
string |
— | Text as #RGB or #RRGGBB; invalid values use automatic contrast. |
style |
Record<string, string | number> |
— | Extra inline styles merged onto the wrapper. |
Generic <Avatar>
When you want to experiment with a custom Canvas2D generator, use the lower-level <Avatar> component. The built-in components above use inline SVG; this generic compatibility component remains canvas-based.
<script setup>
import { Avatar, gradientAvatarAlgorithm } from '@maxnth/gestalt'
</script>
<template>
<Avatar seed="user-123" :size="80" :algorithm="gradientAvatarAlgorithm" />
</template>
An algorithm is just an object with a name and a render function:
import type { AlgorithmRenderContext, AvatarAlgorithm } from '@maxnth/gestalt'
const stripeAlgorithm: AvatarAlgorithm = {
name: 'stripe',
render({ ctx, size, seed }: AlgorithmRenderContext, options) {
ctx.fillStyle = '#3b82f6'
ctx.fillRect(0, 0, size, size / 2)
},
}
Engine helpers
The same gradient engine is exposed as plain functions, which is handy for downloads, server renderers, or anywhere you need an <img> src.
import {
generatePalette,
gradientToDataURL,
seedFromString,
} from '@maxnth/gestalt'
// 512×512 PNG data URL
const src = gradientToDataURL('jane@example.com', { size: 512 })
// Inspect the palette behind a seed
const { seed, colors, harmony } = generatePalette('jane@example.com')
// Convert a string to its numeric seed
const numericSeed = seedFromString('jane@example.com')
| Helper | Description |
|---|---|
drawMeshGradient(ctx, seed, size) |
Paint the raw mesh into a 2D canvas context. |
drawFlowField(ctx, seed, size, opts?) |
Paint a flow field into a 2D canvas context. |
generateFlowFieldLines(seed, size, opts?) |
Generate deterministic flow-field streamlines. |
renderGradient(canvas, seed, opts?) |
Render a seed into a canvas with the soft blur. |
gradientToDataURL(seed, opts?) |
Render and return a data URL. |
gradientToBlob(seed, opts?) |
Render and resolve a Blob. |
generatePalette(seed) |
Get the deterministic colors and harmony for a seed. |
generateDitherGradient(seed, size, variant?) |
Generate palette-quantized dither cells. |
drawDitherGradient(ctx, seed, size, variant?) |
Paint the dither treatment into a 2D canvas. |
seedFromString(input) / toSeed(seed) |
Hash any value into a numeric seed. |
generateGradientSvg(seed, size?, idPrefix?, variant?, mode?) |
Generate a framework-independent gradient SVG document. |
generateInitialsSvg(seed, size?, opts?) |
Generate an initials SVG document. |
generateIdenticonSvg(seed, size?, opts?) |
Generate an identicon SVG document. |
generateFlowFieldSvg(seed, size?, opts?) |
Generate a flow-field SVG document. |
Generator dimensions are rounded and clamped from 1 through 4096; identicon gridSize is clamped from 1 through 64. Built-in color options accept #RGB and #RRGGBB only and normalize them to uppercase #RRGGBB, keeping SVG and Canvas output consistent across runtimes.
SSR
All built-in avatar components render complete inline SVG markup on the server. Their output is deterministic and hydrates without a client-side drawing pass. The lower-level <Avatar> component remains canvas-based for custom Canvas2D algorithms and therefore renders an empty canvas until it mounts in the browser.
License
MIT © Maximilian Nöth
Related Packages
Dithering for the web. Dither any image with Floyd-Steinberg, Atkinson or Bayer,...
Vue library for generating nice user avatar. (Inspired by react-nice-avatar)
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.1.0 | ^3.5.0 | MIT | |
| 0.0.0 | ^3.5.0 | MIT |