Skip to content
Connect an agent

Overview

Flungies

@flung.dev/flungies draws an avatar for anyone from an id: a plush speech bubble caught mid-throw, with two speed lines behind it, a face and sometimes an accessory. The same id gives the same Flungie everywhere, for good.

TL;DR

QuestionAnswer
How do I install it?npm install @flung.dev/flungies
What do I give it?A stable id for the person or the thing, such as a user id. An email or a name can change, and the Flungie with it.
What does it give back?An SVG string, from flungie(). A data: URI, a React component, a custom element and an animated WebGL hero are built on it.
Where does it run?Anywhere JavaScript runs: browsers, Node, Cloudflare Workers, Deno and Bun. The SVG needs no DOM.
What does it depend on?Nothing at run time. React is an optional peer, for the React component.
How many are there?907,200 trait sets, before the sizes, tilts and wobbles that vary on top of them.

What a Flungie is

Flung's mark (G9) is a speech bubble thrown across the frame. A Flungie is that bubble as a character: a plush, felted body with a tail, caught mid-throw, leaning into the throw.

  • The body is the bubble: a soft superellipse with a wedge-shaped tail at its lower back.
  • Two speed lines trail behind it, in two of the brand's colours (peach over sky is the brand pair).
  • A face sits toward the front of the body, the way it is travelling.
  • An accessory is chosen from a creator-themed set: headphones, caps, a beanie, glasses, a party hat, an antenna, a bow, a ring light, a crown, a megaphone, a pencil and a paper plane.
  • A disc behind it is a soft tint on light pages and a deep tone on dark ones. A square tile (variant: 'square') and no ground at all ('bare') are the other variants.

By default the Flungie flies right, with its tail on the left, as the mark does. About a third of Flungies are mirrored. A mirrored Flungie is the same drawing, flipped.

Try it

Type any id to see its Flungie, at every size it simplifies or adds detail at, and the traits it picked. It opens on usr_8f3k2.

Ground

Flungie avatar: Ocean wobbly with crown

  • 16 px
  • 24 px
  • 32 px
  • 48 px
  • 64 px
  • 96 px
Body
Wobbly
Colour
Ocean
Eyes
Ovals
Mouth
None
Accessory
Crown
Side
Tail left, flying right
Speed lines
Lilac and peach
Blush
Off

Install

npm
npm install @flung.dev/flungies

Use

ts
import { flungie, toDataUri, traits, render } from '@flung.dev/flungies'
el.innerHTML = flungie(user.id, { size: 32, theme: 'dark' })    // an SVG stringimg.src = toDataUri(flungie(user.id, { size: 64 }))               // or a data: URIrender(traits(user.id, { accessory: 'crown' }), { size: 128 })    // overrides win over the seed

Seed with a stable ID, such as a user ID, rather than an email or a name that may change.

On a server

The core needs no DOM, so a Worker can serve Flungies as images. A seed always gives the same drawing, so the response can be cached forever.

ts
import { flungie } from '@flung.dev/flungies'
export default {  fetch(request: Request): Response {    const url = new URL(request.url)    const id = url.searchParams.get('id') ?? ''    const size = Math.min(512, Math.max(16, Number(url.searchParams.get('size')) || 64))    return new Response(flungie(id, { size }), {      headers: { 'content-type': 'image/svg+xml', 'cache-control': 'public, max-age=31536000, immutable' },    })  },}

Each SVG's internal ids come from a counter by default. When a server renders a Flungie into HTML that a browser then hydrates, pass the id option (flungie(id, { size, id: 'avatar-1' })) so both sides write the same markup. The React component does this for you with useId.

React

tsx
import { Flungie } from '@flung.dev/flungies/react'
<Flungie seed={user.id} size={32} /><Flungie seed={user.id} size={240} theme="dark" animated />

With animated and a size of 160 or more, the component shows the SVG first, then loads @flung.dev/flungies/gl and swaps in the WebGL hero. Below 160 px, it is always the SVG. The component renders on a server and hydrates without a mismatch.

WebGL hero

ts
import { mountFlungieGL } from '@flung.dev/flungies/gl'
const hero = mountFlungieGL(el, user.id, { size: 320, theme: 'light' })hero.setTheme('dark')hero.setSize(240)hero.dispose()   // stops the loop, releases the GL context and empties el

The hero animates only while it is on screen and the page is visible. Under prefers-reduced-motion it draws one still frame. Without WebGL2, or if the context is lost, it shows the SVG Flungie, and hero.webgl is false.

Custom element

ts
import { defineFlungie } from '@flung.dev/flungies/element'defineFlungie()   // registers <flung-ie>; defineFlungie('my-avatar') uses another name
html
<flung-ie seed="usr_8f3k2" size="40" theme="dark"></flung-ie><flung-ie seed="usr_8f3k2" size="240" animated></flung-ie>

The element takes the attributes seed, size, theme, variant, salt and animated.

Options

OptionForDefault
sizePixels. The drawing simplifies under 32 px and gets its felt texture from 80 px.64
theme'light' or 'dark'. Picks the disc and checks the contrast with the ground.'light'
variant'disc', 'square' (a rounded tile) or 'bare' (no ground).'disc'
texturefalse turns the felt filter off.true
groundWith 'bare', the colour the Flungie sits on, used for the rim check.the theme's page colour
idThe prefix for the SVG's internal ids, so a server and a browser render the same markup.a counter
overridesTraits to set instead of the seed's ({ accessory: 'none' }).none

For other products, these options change which Flungie a seed gives. With all of them unset, a seed gives exactly the Flungie it gives on Flung.

OptionFor
saltMixed into the seed, so the same user ID gives a different Flungie in your product.
paletteBody colours to pick from, instead of COLOURS: { id, label, hex, light: [...discs], dark: [...discs] }, with discs as DISCS keys or #RRGGBB.
accessories{ include: ['none', 'headphones'] } or { exclude: ['crown'] }.
ts
flungie(user.id, { salt: 'acme', accessories: { exclude: ['crown', 'halo'] } })traits(user.id, {}, { salt: 'acme' })   // traits(seed, overrides, config)

Size tiers

A Flungie is drawn for its size: small ones drop detail that would turn to noise, large ones gain a felt texture.

SizeWhat changes
Under 32 pxNo speed lines, shadow, blush, mouth or eye highlights. Eyes are 1.55 times larger, wide eyes become dots, every stroke is at least 0.95 px, the character is scaled up 1.14 times to fill the disc, and fine accessory detail is dropped.
32 to 63 pxAdds the lines, blush, mouth, sheen and highlights.
64 px and upAdds every accessory detail.
80 px and upAdds the felt filter.

The stability promise

A seed's Flungie never changes. People recognise theirs, so a drawing that moved would be a breaking change for every one of them, however much better it looked. Version 1 of the drawing is frozen, and every release is checked against a fixed set of seeds and the exact SVG each one draws. A change that has to alter an existing Flungie ships behind a new version you opt into, and the default stays where it is.

What does change a Flungie is yours to choose: salt, palette and accessories make a set of your own, and overrides sets a trait outright. With none of them, a seed gives exactly the Flungie it gives on Flung.

Traits

Every trait draws from a stream of its own, seeded by the id, so overriding one trait never changes another. The chance is how often a seed picks the option; overrides picks it outright.

Accessories

  • None
  • Headphones
  • Beanie
  • Cap, forward
  • Cap, backward
  • Sunglasses
  • Round glasses
  • Party hat
  • Antenna
  • Bow
  • Ring light
  • Crown
  • Megaphone
  • Pencil
  • Paper plane

Bodies

  • Classic
  • Round
  • Squat
  • Tall
  • Wobbly
  • Puffy

Colours

  • Peach
  • Sky
  • Butter
  • Mint
  • Lilac
  • Coral
  • Sand
  • Cloud
  • Ink
  • Ocean
  • Moss
  • Plum
  • Berry
  • Rust

Eyes

  • Dots
  • Ovals
  • Sleepy
  • Wink
  • Wide
  • Happy arcs

Mouths

  • None
  • Smile
  • Little o
  • Cat
  • Grin

Speed lines

  • Peach and sky
  • Butter and coral
  • Mint and sky
  • Lilac and peach
  • Coral and butter
  • Sky and lilac

Accessories

The creator set. An accessory's colour is picked from a list of its own, the first that stands clear of the body (and, for one above the head, of both themes' discs).

OptionIdChance
Nonenone10%
Headphonesheadphones6.4%
Beaniebeanie6.4%
Cap, forwardcap6.4%
Cap, backwardcapBack6.4%
Sunglassessunglasses6.4%
Round glassesglasses6.4%
Party hatpartyHat6.4%
Antennaantenna6.4%
Bowbow6.4%
Ring lighthalo6.4%
Crowncrown6.4%
Megaphonemegaphone6.4%
Pencilpencil6.4%
Paper planeplane6.4%

Bodies

The bubble, as a soft superellipse with a tail, in six builds.

OptionIdChance
Classicclassic24%
Roundround15%
Squatsquat15%
Talltall15%
Wobblywobbly15%
Puffypuffy15%

Colours

The body colour. Each one sits on a disc chosen for it, a pale tint on a light page and a deep tone on a dark one.

ColourIdHexChance
Peachpeach#FFA5849.6%
Skysky#86C0FF9.6%
Butterbutter#FFD66E7.4%
Mintmint#8EDDB57.4%
Lilaclilac#BFA4FF7.4%
Coralcoral#FF7E6E7.4%
Sandsand#E6C9A05.9%
Cloudcloud#ECEAF25.9%
Inkink#2B28345.9%
Oceanocean#3F7EDB6.7%
Mossmoss#4E9A736.7%
Plumplum#7457C96.7%
Berryberry#D2457E6.7%
Rustrust#C9643B6.7%

Eyes

OptionIdChance
Dotsdots27%
Ovalsovals20%
Sleepysleepy13%
Winkwink11%
Widewide16%
Happy arcshappy13%

Mouths

OptionIdChance
Nonenone30%
Smilesmile30%
Little oo10%
Catcat15%
Gringrin15%

Speed lines

The two lines behind the body, upper and lower. Peach over sky is the brand pair.

PairIdUpperLowerChance
Peach and skybrand#FFA584#86C0FF29%
Butter and coralsunset#FFD66E#FF7E6E14%
Mint and skylagoon#8EDDB5#86C0FF14%
Lilac and peachdusk#BFA4FF#FFA58414%
Coral and butterember#FF7E6E#FFD66E14%
Sky and lilactwilight#86C0FF#BFA4FF14%

Side

Which way the Flungie flies. By default it flies right with its tail on the left, as the mark does.

OptionIdChance
Tail left, flying rightleft69%
Tail right, flying leftright31%

Blush

Rosy cheeks, on or off, picked by the seed.

Accessibility

Every Flungie is an image with a name: the SVG carries role="img" and an aria-label that says what it shows, such as "Flungie avatar: Ocean wobbly with crown", and describe() returns the same words.

Next to the person's name, a Flungie repeats what the name says. There, hide it from screen readers: aria-hidden="true" on the element that holds it, or an empty alt on an <img> with a data: URI.

The animated hero breathes and blinks only while it is on screen and the tab is visible, and under prefers-reduced-motion it draws one still frame. Below 160 px the React component always draws the still SVG.

Also exported

  • TRAITS, every trait with its options, for pickers.
  • combinations(), which returns 907,200 discrete trait sets.
  • The tables COLOURS, DISCS, SHAPES, EYES, MOUTHS, ACCESSORIES, ACC_COLOURS, LINE_PAIRS and SIDES.
  • colours() and geometry(), for renderers of your own.
  • toSvgElement(svg), which parses an SVG string into an element to append, for pages that may not assign innerHTML.
  • describe(), the accessible name, which every SVG also carries as its aria-label.
  • The colour helpers contrast, distance, luminance and mix.

See also

DocWhy
BrandThe mark the Flungies are drawn from, its colours and its type
TypeScript SDKThe client for the Flung API
Workspace marksThe squared marks the same package draws for workspaces
Connect panelLet people connect their accounts from your page