@ignaciocabeza/bitface
v1.1.1
Published
Pixel art SVG avatar/face generator with zero dependencies
Maintainers
Readme
bitface
Tiny pixel art avatars that pack a punch.
Generate unique 16x16 pixel art faces as clean SVGs — no dependencies, no canvas, no nonsense. Works everywhere: browsers, servers, React, Vue, Svelte, or just plain JavaScript.
+--+--+--+--+--+--+
| |##|##|##|##| |
+--+--+--+--+--+--+
|##| |##|##| |##| <-- that's a face
+--+--+--+--+--+--+
| | | > < | | |
+--+--+--+--+--+--+
| | | \_/ | | |
+--+--+--+--+--+--+Why bitface?
- Zero dependencies — just your code and some math
- Tiny output — SVGs are a handful of
<rect>elements, optimized by merging adjacent pixels - Deterministic — same config = same face, every time
- Framework agnostic — first-class React, Vue, and Svelte support, but works without any of them
- Animations — idle, blink, talk, and emote presets built in
- 9 customizable features — face shape, eyes, eyebrows, mouth, nose, ears, hair, beard, and accessories
- Color presets — 6 skin tones, 13 hair colors, 4 eye colors, or use any hex color
Install
npm install @ignaciocabeza/bitfaceQuick start
import { generateFace, generateRandomConfig } from '@ignaciocabeza/bitface'
// Generate a random avatar
const svg = generateFace()
// Generate with a specific config
const config = generateRandomConfig()
config.hair = 'mohawk'
config.accessories = 'sunglasses'
const coolAvatar = generateFace(config)That's it. coolAvatar is an SVG string you can drop into any innerHTML, save to a file, or send over an API.
Framework usage
React
import { Avatar } from '@ignaciocabeza/bitface/react'
function Profile() {
return (
<Avatar
config={{
faceShape: 'round',
eyes: 'big',
eyebrows: 'arched',
mouth: 'smile',
nose: 'button',
ears: 'small',
hair: 'curly',
hairColor: 'auburn',
skinColor: 'medium',
accessories: 'glasses',
}}
size={128}
animation="idle"
/>
)
}Hooks:
import { useAvatar, useAnimatedAvatar } from '@ignaciocabeza/bitface/react'
// Static avatar
const svg = useAvatar(config)
// Animated avatar
const svg = useAnimatedAvatar(config, 'blink')Vue
<script setup>
import { Avatar } from '@ignaciocabeza/bitface/vue'
const config = {
faceShape: 'oval',
eyes: 'round',
eyebrows: 'thick',
mouth: 'grin',
nose: 'pointy',
ears: 'pointed',
hair: 'spiky',
hairColor: 'blue',
beard: 'goatee',
}
</script>
<template>
<Avatar :config="config" :size="128" animation="talk" />
</template>Composables:
import { useAvatar, useAnimatedAvatar } from '@ignaciocabeza/bitface/vue'
const svg = useAvatar(() => config)
const animatedSvg = useAnimatedAvatar(() => config, () => 'emote')Svelte
<script>
import Avatar from '@ignaciocabeza/bitface/svelte'
const config = {
faceShape: 'heart',
eyes: 'wink',
eyebrows: 'thin',
mouth: 'smirk',
nose: 'small',
ears: 'elf',
hair: 'ponytail',
hairColor: 'pink',
accessories: 'earrings',
}
</script>
<Avatar {config} size={128} animation="idle" />Node.js / server-side
import { generateFace } from '@ignaciocabeza/bitface'
import { writeFileSync } from 'fs'
const svg = generateFace({
faceShape: 'square',
eyes: 'angry',
eyebrows: 'angry',
mouth: 'frown',
nose: 'wide',
ears: 'big',
hair: 'mohawk',
hairColor: 'red',
beard: 'full',
backgroundColor: 'transparent',
})
writeFileSync('avatar.svg', svg)API
generateFace(config?)
Returns an SVG string. Pass a FaceConfig for a specific face, or call with no arguments for a random one.
generateRandomConfig()
Returns a random FaceConfig object with all fields populated.
getAvailableParts()
Returns all part variants grouped by category:
{
faceShape: ['round', 'oval', 'square', 'heart', 'long', 'diamond', 'wide'],
eyes: ['big', 'small', 'narrow', 'round', 'wink', 'happy', 'angry', 'dots', 'sleepy', 'cross', 'heart'],
eyebrows: ['thick', 'thin', 'arched', 'angry', 'worried', 'unibrow', 'none'],
mouth: ['smile', 'frown', 'open', 'flat', 'teeth', 'smirk', 'grin', 'tongue', 'oh'],
nose: ['small', 'pointy', 'wide', 'button', 'long', 'snub'],
ears: ['small', 'big', 'pointed', 'elf', 'none'],
hair: ['short', 'long', 'curly', 'mohawk', 'bald', 'ponytail', 'spiky', 'bob', 'afro', 'bangs'],
beard: ['stubble', 'goatee', 'full', 'mustache', 'handlebar', 'none'],
accessories: ['glasses', 'sunglasses', 'hat', 'headband', 'earrings', 'none'],
}getPartThumbnail(category, name, skinColor?, hairColor?, eyeColor?)
Returns an SVG string for a single part preview. Useful for building part pickers.
getAnimationNames()
Returns ['idle', 'talk', 'blink', 'emote'].
FaceConfig
| Property | Required | Values | Default |
|---|---|---|---|
| faceShape | Yes | round oval square heart long diamond wide | — |
| eyes | Yes | big small narrow round wink happy angry dots sleepy cross heart | — |
| eyebrows | Yes | thick thin arched angry worried unibrow none | — |
| mouth | Yes | smile frown open flat teeth smirk grin tongue oh | — |
| nose | Yes | small pointy wide button long snub | — |
| ears | Yes | small big pointed elf none | — |
| hair | Yes | short long curly mohawk bald ponytail spiky bob afro bangs | — |
| beard | No | stubble goatee full mustache handlebar none | none |
| accessories | No | glasses sunglasses hat headband earrings none | none |
| skinColor | No | Preset name or hex color | medium |
| hairColor | No | Preset name or hex color | black |
| eyeColor | No | Preset name or hex color | brown |
| backgroundColor | No | Hex color or transparent | #87CEEB |
Color presets
Skin
| Preset | Color |
|---|---|
| light | #FDDCB5 |
| medium | #E8B88A |
| tan | #C8956C |
| brown | #8D5E3C |
| dark | #5C3A1E |
| pale | #FFF0E0 |
Hair
black brown blonde red gray white auburn strawberry platinum pink blue purple teal
Eyes
brown blue green gray
You can also pass any valid hex color (e.g. #FF6600) for any color property.
Animations
Built-in animation presets that cycle through face variations:
| Name | Duration | What it does |
|---|---|---|
| idle | 3.3s | Rests, then does a quick happy-eyes blink |
| blink | 3s | Natural eye blink cycle |
| talk | 1s | Mouth shape variations |
| emote | 1.6s | Eyebrow raise + mouth reaction |
Pass them as strings to any framework component:
<Avatar config={config} animation="blink" />Or use the hooks/composables for more control:
const svg = useAnimatedAvatar(config, 'talk')You can also define custom animation sequences:
const myAnimation = {
name: 'custom',
frames: [
{ duration: 500, overrides: {} },
{ duration: 200, overrides: { eyes: 'wink', mouth: 'smirk' } },
{ duration: 300, overrides: { eyes: 'big', mouth: 'grin' } },
],
}How it works
Each avatar is a 16x16 pixel grid. Parts (face, eyes, hair, etc.) are stamped onto the grid in layers, bottom to top:
Layer 0: Face shape (base)
Layer 1: Ears
Layer 2: Nose
Layer 3: Mouth
Layer 4: Eyes
Layer 5: Eyebrows
Layer 6: Hair
Layer 7: Beard
Layer 8: AccessoriesEach part is defined as a small pixel array where numbers map to color roles (1 = skin, 2 = shadow, etc.). The renderer resolves colors, stamps parts onto the grid, then converts the grid to an optimized SVG by merging horizontally adjacent same-color pixels into wider <rect> elements.
The result is a clean, scalable SVG with crisp pixel-art rendering — no blurry edges, no canvas required.
License
MIT — do whatever you want with it.
