expovatar
v0.0.1
Published
Deterministic avatars from any string for React Native + Expo — gradient & dither, Skia-rendered. A port of hashvatar.
Downloads
20
Maintainers
Readme
expovatar
Deterministic avatars from any string — for React Native + Expo. Wallet address, username, UUID → the same avatar every time. Two modes: gradient (diffuse blurred blends) and dither (Bayer halftone). Skia‑rendered, crisp at any size.
expovatar is a React Native port of hashvatar: the same FNV‑1a → mulberry32 seeding, OKLCH palette, and generation logic, with the canvas renderers rebuilt on @shopify/react-native-skia.
import { Expovatar } from 'expovatar'
<Expovatar hash="favourafula.bnb" size={48} />
<Expovatar hash="chidinma" size={64} mode="dither" />
<Expovatar hash="0x742d…f44e" size={64} tones={['hotpink', '#00ff99']} animated />Install
Skia is a native module — expovatar runs on an Expo dev client / prebuild / EAS build, not Expo Go.
npm install expovatar
npx expo install @shopify/react-native-skia@shopify/react-native-skia (≥1.5) is the only peer. No Reanimated / Gesture Handler needed — animation uses a plain requestAnimationFrame loop.
Usage
import { Expovatar } from 'expovatar'
function Row() {
return (
<>
<Expovatar hash="favourafula.bnb" size={64} />
<Expovatar hash="adaeze" size={40} mode="dither" />
<Expovatar hash="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" size={40} tones={['#6366f1']} />
</>
)
}The component renders a circle by default (rounded); pass rounded={false} for a square.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| hash | string | — | Any string. Same string → same avatar. |
| size | number | 64 | Square size in px. |
| mode | 'gradient' \| 'dither' | 'gradient' | Render style. |
| animated | boolean | false | Animate (drifting blends / swirling dither). |
| dotScale | number | auto | Dither cell size; scales with size if omitted. |
| tones | string[] | — | Restrict the palette — hex, oklch(l c h), or common CSS names. |
| rounded | boolean | true | Clip to a circle. |
| style | ViewStyle | — | Style on the wrapping view. |
Modes
Gradient — six irregular polygons, each blurred (MaskFilter) and composited with a blend mode (SrcOver / Overlay / SoftLight) over a base color, for a soft diffuse blob. Monotone from the hash by default.
Dither — an 8×8 Bayer‑matrix halftone: a directional gradient thresholded into crisp cells of one color over another. Great for a retro / pixelated identity.
<Expovatar hash="emeka.bnb" mode="gradient" />
<Expovatar hash="emeka.bnb" mode="dither" dotScale={3} />Tones
Restrict the palette to a hue family. Without tones, the avatar is monotone from the hash.
<Expovatar hash="ngozi" tones={['hotpink']} /> // one family
<Expovatar hash="ngozi" tones={['#ff6b6b', '#4ecdc4']} /> // multiple
<Expovatar hash="ngozi" tones={['oklch(0.65 0.25 310)']} /> // oklchAccepts hex (#ff69b4 / ff69b4), oklch(l c h), and common CSS color names (red, hotpink, teal, …). Unknown names fall back to the monotone palette; hex and oklch() always work.
Utilities
Everything deterministic is exported for custom rendering or theming — none of it touches Skia:
import { hashToColors, hashToSeeds, oklchToHex } from 'expovatar'
const colors = hashToColors('favourafula.bnb', undefined, 4) // OKLCH[]
const hexes = colors.map(oklchToHex) // ['#…', …]
const seeds = hashToSeeds('favourafula.bnb', 8) // 8 numbers in [0,1)hashToColors(hash, tones?, count?)→ the generated OKLCH palette.hashToSeeds(hash, count)→countdeterministic numbers in[0, 1).oklchToHex/oklchToCss/rgbToOklch/parseTone→ color helpers.drawGradient/drawDither→ low‑level Skia painters (draw into anySkCanvas/PictureRecorder) if you want to composite avatars yourself.
How it works
hash → FNV‑1a → mulberry32 seeds → an OKLCH palette → a Skia picture. The picture is recorded once and memoized; animated re‑records on a requestAnimationFrame loop. Same string always yields the same seeds, palette, and picture.
The hash / color / generation logic ports near‑verbatim from hashvatar. The web renderers (<canvas> 2D, ctx.filter, globalCompositeOperation, ImageData) become Skia: MaskFilter blur, BlendMode, and crisp drawRect cells.
Development
npm install
npm test # vitest — pure-core determinism
npm run typecheck
npm run build # tsup → dist (ESM + CJS + d.ts)License
MIT. Ported from hashvatar (MIT, © Médhy Chabour) — see LICENSE.
