pictoguys 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +397 -0
- package/dist/character-DAS3IMm1.d.cts +141 -0
- package/dist/character-DAS3IMm1.d.ts +141 -0
- package/dist/chunk-2W2JD54I.js +21 -0
- package/dist/chunk-74VFD4AY.js +109 -0
- package/dist/chunk-MHPZBNYL.js +359 -0
- package/dist/chunk-RSKU5HRZ.js +18 -0
- package/dist/core.cjs +409 -0
- package/dist/core.d.cts +36 -0
- package/dist/core.d.ts +36 -0
- package/dist/core.js +3 -0
- package/dist/index.cjs +536 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/react.cjs +504 -0
- package/dist/react.d.cts +20 -0
- package/dist/react.d.ts +20 -0
- package/dist/react.js +3 -0
- package/dist/rng.cjs +24 -0
- package/dist/rng.d.cts +4 -0
- package/dist/rng.d.ts +4 -0
- package/dist/rng.js +1 -0
- package/package.json +76 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 C3B
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/logo.png" alt="picto" width="340" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/latte.gif" width="92" alt="Latte dancing" />
|
|
7
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/kiwi.gif" width="92" alt="Kiwi dancing" />
|
|
8
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/goose.gif" width="92" alt="Goose dancing" />
|
|
9
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/waffle.gif" width="92" alt="Waffle dancing" />
|
|
10
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/mango.gif" width="92" alt="Mango dancing" />
|
|
11
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/udon.gif" width="92" alt="Udon dancing" />
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<strong>Give it a name. Get a little guy.</strong><br/>
|
|
16
|
+
Tiny React library for procedural SVG characters (I call them <em>pictos</em>).
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="https://img.shields.io/badge/version-0.1.0-ff69b4" alt="version" />
|
|
21
|
+
<img src="https://img.shields.io/badge/TypeScript-ready-3178c6?logo=typescript&logoColor=white" alt="typescript" />
|
|
22
|
+
<img src="https://img.shields.io/badge/React-%E2%89%A5%2017-61dafb?logo=react&logoColor=white" alt="react" />
|
|
23
|
+
<img src="https://img.shields.io/badge/runtime%20deps-0-22c55e" alt="zero deps" />
|
|
24
|
+
<img src="https://img.shields.io/badge/license-MIT-22c55e" alt="license" />
|
|
25
|
+
<img src="https://img.shields.io/badge/pictos-%E2%88%9E-8b5cf6" alt="infinite pictos" />
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## What is this?
|
|
31
|
+
|
|
32
|
+
`pictoguys` makes cute little SVG characters out of thin air.
|
|
33
|
+
|
|
34
|
+
You hand it a number or a word. It hands you back a character: a colored body,
|
|
35
|
+
some eyes, eyebrows, and (if you want one) a background tile. The same word
|
|
36
|
+
always makes the exact same character, so "Bloop" is always Bloop, on every
|
|
37
|
+
device, forever.
|
|
38
|
+
|
|
39
|
+
Think of it like a profile-picture generator, except:
|
|
40
|
+
|
|
41
|
+
- the art is real vector SVG (crisp at any size, never blurry),
|
|
42
|
+
- there are no image files to download (the parts are baked into the library),
|
|
43
|
+
- and the little guys can blink, hop, breathe, and dance.
|
|
44
|
+
|
|
45
|
+
No design skills needed. You do not draw anything. You just pick a seed.
|
|
46
|
+
|
|
47
|
+
## What is a "picto"?
|
|
48
|
+
|
|
49
|
+
A **picto** is one character. That is the whole vocabulary you need.
|
|
50
|
+
|
|
51
|
+
Every picto is built from a few parts that get mixed and recolored:
|
|
52
|
+
|
|
53
|
+
| Part | Choices |
|
|
54
|
+
| ----------- | ---------------------------------------- |
|
|
55
|
+
| body color | 10 hand-picked colors, plus blended ones |
|
|
56
|
+
| body shape | 4 shapes |
|
|
57
|
+
| eyes | single, double, or triple |
|
|
58
|
+
| eye coloring| plain, two-tone, or rainbow |
|
|
59
|
+
| background | 5 tiles |
|
|
60
|
+
|
|
61
|
+
Pick those yourself, or let a seed pick them for you. Either way you get a picto.
|
|
62
|
+
|
|
63
|
+
## Install
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npm install pictoguys
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
For React components, bring your own `react` (version 17 or newer). For SVG-only
|
|
70
|
+
usage, import from `pictoguys/core` and React is not loaded. There are zero
|
|
71
|
+
runtime dependencies.
|
|
72
|
+
|
|
73
|
+
| Import path | Use it for |
|
|
74
|
+
| ----------- | ---------- |
|
|
75
|
+
| `pictoguys` | React projects that want `<Picto />` plus the core helpers |
|
|
76
|
+
| `pictoguys/react` | Only the React component and its props |
|
|
77
|
+
| `pictoguys/core` | SVG strings, characters, presets, and catalog helpers without React |
|
|
78
|
+
| `pictoguys/rng` | The tiny deterministic RNG only |
|
|
79
|
+
|
|
80
|
+
## Your first picto
|
|
81
|
+
|
|
82
|
+
Drop this into any React component:
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import { Picto } from 'pictoguys'
|
|
86
|
+
|
|
87
|
+
export default function App() {
|
|
88
|
+
return <Picto seed="Bloop" size={120} />
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
That is it. You just rendered Bloop.
|
|
93
|
+
|
|
94
|
+
`seed` can be a word (`"Bloop"`) or a number (`7`). Same seed, same picto, every
|
|
95
|
+
single time. Change the word, get a different friend.
|
|
96
|
+
|
|
97
|
+
## Meet some pictos
|
|
98
|
+
|
|
99
|
+
Here is what a handful of names look like. Try your own.
|
|
100
|
+
|
|
101
|
+
| | | | | | |
|
|
102
|
+
|:-:|:-:|:-:|:-:|:-:|:-:|
|
|
103
|
+
| <img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/bloop.svg" width="76" alt="Bloop"/> | <img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/mochi.svg" width="76" alt="Mochi"/> | <img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/zorp.svg" width="76" alt="Zorp"/> | <img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/waffle.svg" width="76" alt="Waffle"/> | <img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/gizmo.svg" width="76" alt="Gizmo"/> | <img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/noodle.svg" width="76" alt="Noodle"/> |
|
|
104
|
+
| `"Bloop"` | `"Mochi"` | `"Zorp"` | `"Waffle"` | `"Gizmo"` | `"Noodle"` |
|
|
105
|
+
| <img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/tofu.svg" width="76" alt="Tofu"/> | <img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/bubbles.svg" width="76" alt="Bubbles"/> | <img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/sprocket.svg" width="76" alt="Sprocket"/> | <img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/pickle.svg" width="76" alt="Pickle"/> | <img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/goose.svg" width="76" alt="Goose"/> | <img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/wizard.svg" width="76" alt="Wizard"/> |
|
|
106
|
+
| `"Tofu"` | `"Bubbles"` | `"Sprocket"` | `"Pickle"` | `"Goose"` | `"Wizard"` |
|
|
107
|
+
|
|
108
|
+
Waffle and Bubbles got fancy multi-colored eyes. Lucky.
|
|
109
|
+
|
|
110
|
+
## Three ways to make a picto
|
|
111
|
+
|
|
112
|
+
**1. By name (a word).** Great for usernames, emails, anything text.
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
<Picto seed="ada@example.com" />
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**2. By number.** Great when you just want "give me number 42".
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
<Picto seed={42} />
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**3. By hand.** Want an exact look? Spell it out. Anything you leave out gets
|
|
125
|
+
filled in for you.
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
<Picto
|
|
129
|
+
config={{
|
|
130
|
+
color: 'pink', // 'blue' 'cian' 'gray' 'green' 'lime'
|
|
131
|
+
// 'orange' 'pink' 'purple' 'red' 'yellow'
|
|
132
|
+
shape: 2, // 1, 2, 3, or 4
|
|
133
|
+
eyes: 'triple', // 'single' | 'double' | 'triple'
|
|
134
|
+
mode: 'triad', // 'mono' (plain) | 'hetero' (two-tone) | 'triad' (rainbow)
|
|
135
|
+
bg: 4, // 1 to 5
|
|
136
|
+
}}
|
|
137
|
+
/>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
<p align="center">
|
|
141
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/custom.svg" width="120" alt="custom pink triple-eye picto" />
|
|
142
|
+
</p>
|
|
143
|
+
|
|
144
|
+
Using TypeScript? Import the config type for full autocomplete:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import type { CharConfig } from 'pictoguys'
|
|
148
|
+
|
|
149
|
+
const cfg: CharConfig = { color: 'blue', eyes: 'double' }
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Constraints and branding (the fun part)
|
|
153
|
+
|
|
154
|
+
Here is the one rule that gives you total control. For **every** setting:
|
|
155
|
+
|
|
156
|
+
| You write | You get |
|
|
157
|
+
| ------------------ | -------------------------------- |
|
|
158
|
+
| nothing (omit it) | fully random (picked by the seed)|
|
|
159
|
+
| one value | locked to that value |
|
|
160
|
+
| an array of values | random, but only from that set |
|
|
161
|
+
|
|
162
|
+
Mix and match freely. Add a `seed` (like a user id) and the random parts become
|
|
163
|
+
stable per person.
|
|
164
|
+
|
|
165
|
+
### "Use my brand color on every avatar"
|
|
166
|
+
|
|
167
|
+
Set your brand once with `picto.preset(...)`, then stamp out users. Each person
|
|
168
|
+
keeps your color but gets their own body and face. You can pass a brand **hex**
|
|
169
|
+
straight in, and a matching darker shade is generated for the gradient.
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
import { picto, Picto } from 'pictoguys'
|
|
173
|
+
|
|
174
|
+
const brand = picto.preset({ color: '#19c37d' }) // your green
|
|
175
|
+
|
|
176
|
+
function Avatar({ userId }) {
|
|
177
|
+
const me = React.useMemo(() => brand.character(userId), [userId])
|
|
178
|
+
return <Picto char={me} size={96} />
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
<p align="center">
|
|
183
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/brand-1.svg" width="84" alt="" />
|
|
184
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/brand-2.svg" width="84" alt="" />
|
|
185
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/brand-3.svg" width="84" alt="" />
|
|
186
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/brand-4.svg" width="84" alt="" />
|
|
187
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/brand-5.svg" width="84" alt="" />
|
|
188
|
+
</p>
|
|
189
|
+
|
|
190
|
+
<p align="center"><em>Same brand green. Different bodies and faces. One per user.</em></p>
|
|
191
|
+
|
|
192
|
+
### "Always this body, random colors, no random face"
|
|
193
|
+
|
|
194
|
+
Lock the shape and the face, let the color come from the seed:
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
<Picto
|
|
198
|
+
config={{
|
|
199
|
+
seed: userId,
|
|
200
|
+
shape: 2, // always this body
|
|
201
|
+
eyes: 'double', // always this face
|
|
202
|
+
brow: 'double_1', // "
|
|
203
|
+
mode: 'mono', // always plain eyes
|
|
204
|
+
// color is left out, so it is the only thing that changes per user
|
|
205
|
+
}}
|
|
206
|
+
/>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
<p align="center">
|
|
210
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/shapelock-1.svg" width="84" alt="" />
|
|
211
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/shapelock-2.svg" width="84" alt="" />
|
|
212
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/shapelock-3.svg" width="84" alt="" />
|
|
213
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/shapelock-4.svg" width="84" alt="" />
|
|
214
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/shapelock-5.svg" width="84" alt="" />
|
|
215
|
+
</p>
|
|
216
|
+
|
|
217
|
+
<p align="center"><em>Same shape, same plain face. Only the color rolls.</em></p>
|
|
218
|
+
|
|
219
|
+
### Random, but only from a set
|
|
220
|
+
|
|
221
|
+
Want variety, but inside guardrails? Pass arrays:
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
// only ever shapes 1 or 3, only double or triple eyes, only your two greens
|
|
225
|
+
<Picto
|
|
226
|
+
config={{
|
|
227
|
+
seed: userId,
|
|
228
|
+
shape: [1, 3],
|
|
229
|
+
eyes: ['double', 'triple'],
|
|
230
|
+
color: ['green', 'lime'],
|
|
231
|
+
}}
|
|
232
|
+
/>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Brand colors, spelled out
|
|
236
|
+
|
|
237
|
+
`color` accepts an anchor name (`'blue'`), a brand hex (`'#19c37d'`, auto-gradient),
|
|
238
|
+
or an array of either. For full manual control, set both gradient stops yourself
|
|
239
|
+
with `light` and `dark`. Want to preview the gradient a hex would make?
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
import { picto } from 'pictoguys'
|
|
243
|
+
|
|
244
|
+
picto.gradient('#19c37d') // { light: '#19c37d', dark: '#003329' }
|
|
245
|
+
picto.gradient(140) // a gradient for hue 140 degrees
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
> **Tip:** pass something stable as the `seed` (a user id, an email, a username)
|
|
249
|
+
> so the same person always lands on the same picto.
|
|
250
|
+
|
|
251
|
+
## Make them move
|
|
252
|
+
|
|
253
|
+
Two ways, pick whichever feels easier.
|
|
254
|
+
|
|
255
|
+
**The easy way: just ask for an animation.**
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
<Picto seed="Gizmo" animate="breath" />
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Animations available: `"blink"`, `"jump"`, `"breath"`, `"dance"`.
|
|
262
|
+
`breath` and `dance` loop forever. `blink` and `jump` play once.
|
|
263
|
+
|
|
264
|
+
**The hands-on way: tell a specific picto to do something.**
|
|
265
|
+
|
|
266
|
+
First make a picto with `picto.character(...)`, then call methods on it:
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
import { picto, Picto } from 'pictoguys'
|
|
270
|
+
|
|
271
|
+
function Mascot() {
|
|
272
|
+
const guy = React.useMemo(() => picto.character('Gizmo'), [])
|
|
273
|
+
|
|
274
|
+
return (
|
|
275
|
+
<>
|
|
276
|
+
<Picto char={guy} size={140} />
|
|
277
|
+
<button onClick={() => guy.blink()}>blink</button>
|
|
278
|
+
<button onClick={() => guy.dance()}>dance</button>
|
|
279
|
+
<button onClick={() => guy.stop()}>chill</button>
|
|
280
|
+
</>
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
guy.blink() // one blink
|
|
287
|
+
guy.jump() // one hop
|
|
288
|
+
guy.breath() // breathe (loops)
|
|
289
|
+
guy.dance() // dance (loops)
|
|
290
|
+
guy.stop() // freeze
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
> **Heads up for beginners:** `guy.blink()` works by poking the `<Picto char={guy} />`
|
|
294
|
+
> on screen. If that picto is not currently rendered, the call simply does
|
|
295
|
+
> nothing (no crash, no error, just a no-op). So render it first, then animate it.
|
|
296
|
+
|
|
297
|
+
## All the props
|
|
298
|
+
|
|
299
|
+
`<Picto>` accepts these. Everything is optional.
|
|
300
|
+
|
|
301
|
+
| Prop | Type | Default | What it does |
|
|
302
|
+
| ------------ | -------------------------------------- | ------- | ----------------------------------------- |
|
|
303
|
+
| `seed` | `number \| string` | `0` | Build a picto from a number or a word. |
|
|
304
|
+
| `config` | `CharConfig` | none | Build a picto from exact settings. |
|
|
305
|
+
| `char` | `Character` | none | Use a picto you already made (wins). |
|
|
306
|
+
| `size` | `number` | `120` | Width and height, in pixels. |
|
|
307
|
+
| `background` | `boolean` | `false` | Set `true` to add a background tile. |
|
|
308
|
+
| `animate` | `"blink" \| "jump" \| "breath" \| "dance"` | none | Play an animation on loop or once. |
|
|
309
|
+
|
|
310
|
+
Any normal `<span>` prop works too (`className`, `style`, `onClick`, and so on),
|
|
311
|
+
because that is what `<Picto>` renders into.
|
|
312
|
+
|
|
313
|
+
Pictos are see-through by default, so they sit nicely on top of anything. Want a
|
|
314
|
+
colored tile behind one instead? Flip one switch:
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
<Picto seed="Bloop" /> {/* bare, the default */}
|
|
318
|
+
<Picto seed="Bloop" background /> {/* with a background tile */}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
<p align="center">
|
|
322
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/bloop.svg" width="120" alt="Bloop, bare" />
|
|
323
|
+
|
|
324
|
+
<img src="https://raw.githubusercontent.com/ctresb/picto/main/assets/bloop-bg.svg" width="120" alt="Bloop with a background tile" />
|
|
325
|
+
</p>
|
|
326
|
+
|
|
327
|
+
## Using it outside React
|
|
328
|
+
|
|
329
|
+
A picto is just an SVG string under the hood, so you can grab that string and do
|
|
330
|
+
whatever you want with it (emails, server rendering, saving to a file).
|
|
331
|
+
|
|
332
|
+
```ts
|
|
333
|
+
import { picto } from 'pictoguys/core'
|
|
334
|
+
|
|
335
|
+
const svg = picto.character('Bloop').svg() // bare (default)
|
|
336
|
+
// -> "<svg viewBox=\"0 0 40 40\" ...>...</svg>"
|
|
337
|
+
|
|
338
|
+
const withTile = picto.character('Bloop').svg({ background: true }) // add a tile
|
|
339
|
+
const prefixed = picto.character('Bloop').svg({ uid: 'a_' }) // custom id prefix
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
You can also read what a picto turned out to be:
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
const guy = picto.character('Bloop')
|
|
346
|
+
guy.config
|
|
347
|
+
// { color: 'gen319', shape: '4', eyes: 'single', mode: 'mono', ... }
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
The `svg()` output is deterministic, and every id inside is prefixed, so you can
|
|
351
|
+
drop many pictos into one page without their gradients or filters fighting.
|
|
352
|
+
|
|
353
|
+
## Bonus: just the random number maker
|
|
354
|
+
|
|
355
|
+
The seeded randomness that powers picto lives on its own tiny path, with **none**
|
|
356
|
+
of the character art attached. Handy if you only want stable, repeatable random
|
|
357
|
+
numbers:
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
import { mulberry32, hashSeed } from 'pictoguys/rng'
|
|
361
|
+
|
|
362
|
+
const random = mulberry32(hashSeed('any-string'))
|
|
363
|
+
random() // a number 0..1, the same every time for that string
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Importing `pictoguys/rng` pulls in well under 1 KB. Importing the full library
|
|
367
|
+
includes the character art (that art is the whole point, so it ships with it).
|
|
368
|
+
|
|
369
|
+
## How it works (the 20 second version)
|
|
370
|
+
|
|
371
|
+
1. Your seed goes through a small, predictable shuffler.
|
|
372
|
+
2. The shuffle picks a color, a shape, eyes, eyebrows, and a background.
|
|
373
|
+
3. Those SVG parts get recolored in OKLCH color space (so the colors always look
|
|
374
|
+
nice together, not muddy) and stitched into one SVG.
|
|
375
|
+
4. React drops that SVG on the page. Animations are just smooth CSS transforms on
|
|
376
|
+
the body or the eyes.
|
|
377
|
+
|
|
378
|
+
Same seed in means same picto out. No randomness leaks, no surprises.
|
|
379
|
+
|
|
380
|
+
## FAQ
|
|
381
|
+
|
|
382
|
+
**Is it really the same picto every time?**
|
|
383
|
+
Yes. "Bloop" is Bloop on your laptop, your phone, and your friend's machine.
|
|
384
|
+
|
|
385
|
+
**Do I need to download or host any images?**
|
|
386
|
+
No. The parts are inside the package. One `npm install` and you are done.
|
|
387
|
+
|
|
388
|
+
**Can I get a totally specific look?**
|
|
389
|
+
Yes, use `config={{ ... }}` and set exactly what you want.
|
|
390
|
+
|
|
391
|
+
**How big is it?**
|
|
392
|
+
The art data is around 120 KB before gzip. It compresses well, and it is the
|
|
393
|
+
actual content of the library, so there is nothing to fetch separately.
|
|
394
|
+
|
|
395
|
+
## License
|
|
396
|
+
|
|
397
|
+
MIT. Go make a thousand little guys.
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/** Artist body colors, e.g. "blue", "pink". */
|
|
2
|
+
declare const COLORS: string[];
|
|
3
|
+
/** Body shape variants, "1".."4". */
|
|
4
|
+
declare const SHAPES: string[];
|
|
5
|
+
/** Background keys, "1".."5". */
|
|
6
|
+
declare const BGS: string[];
|
|
7
|
+
/** Eye kinds that have both eyes and matching eyebrows: "single" | "double" | "triple". */
|
|
8
|
+
declare const PREFIXES: string[];
|
|
9
|
+
interface PalEntry {
|
|
10
|
+
name: string;
|
|
11
|
+
light: string;
|
|
12
|
+
dark: string;
|
|
13
|
+
gen: boolean;
|
|
14
|
+
}
|
|
15
|
+
declare function buildPalette(selColors: string[], extra: number): PalEntry[];
|
|
16
|
+
interface Tuning {
|
|
17
|
+
/** feature (outline) lightness, 10..45 */
|
|
18
|
+
featL: number;
|
|
19
|
+
/** feature tint chroma, 0..12 */
|
|
20
|
+
featC: number;
|
|
21
|
+
/** lit pupil lightness, 60..95 */
|
|
22
|
+
litL: number;
|
|
23
|
+
/** pupil chroma, 4..22 */
|
|
24
|
+
pupC: number;
|
|
25
|
+
}
|
|
26
|
+
declare const DEFAULT_TUNING: Tuning;
|
|
27
|
+
interface ComposeArgs {
|
|
28
|
+
light: string;
|
|
29
|
+
dark: string;
|
|
30
|
+
shape: string;
|
|
31
|
+
bg: string;
|
|
32
|
+
eye: string;
|
|
33
|
+
brow: string;
|
|
34
|
+
mode: string;
|
|
35
|
+
tuning: Tuning;
|
|
36
|
+
uid: string;
|
|
37
|
+
background: boolean;
|
|
38
|
+
}
|
|
39
|
+
declare function compose(a: ComposeArgs): string;
|
|
40
|
+
type Mode = 'mono' | 'hetero' | 'triad';
|
|
41
|
+
type EyeKind = 'single' | 'double' | 'triple';
|
|
42
|
+
/**
|
|
43
|
+
* A constraint value. Three ways to use any field:
|
|
44
|
+
* - omit it -> fully random (picked from the seed)
|
|
45
|
+
* - one value -> locked to that value
|
|
46
|
+
* - an array of values -> random, but only from that set (picked from the seed)
|
|
47
|
+
*/
|
|
48
|
+
type OneOrMany<T> = T | readonly T[];
|
|
49
|
+
interface CharConfig {
|
|
50
|
+
/** Explicit seed. Omit to derive a stable seed from the other fields. */
|
|
51
|
+
seed?: number | string;
|
|
52
|
+
/**
|
|
53
|
+
* Body color. An anchor name ("blue"), OR a brand hex ("#19c37d") which gets a
|
|
54
|
+
* matching darker shade auto-generated. Array = pick one at random.
|
|
55
|
+
*/
|
|
56
|
+
color?: OneOrMany<string>;
|
|
57
|
+
/** Body hue in degrees. Array = pick one. Used when `color` is absent. */
|
|
58
|
+
hue?: OneOrMany<number>;
|
|
59
|
+
/** Explicit gradient stops. Full manual lock, wins over color/hue. */
|
|
60
|
+
light?: string;
|
|
61
|
+
dark?: string;
|
|
62
|
+
/** Body shape, 1..4. Array = pick one. */
|
|
63
|
+
shape?: OneOrMany<number | string>;
|
|
64
|
+
/** Eye kind. Array = pick one. */
|
|
65
|
+
eyes?: OneOrMany<EyeKind>;
|
|
66
|
+
/** Exact eye file key, e.g. "double_2" (implies `eyes`). Array = pick one. */
|
|
67
|
+
eye?: OneOrMany<string>;
|
|
68
|
+
/** Exact eyebrow file key. Array = pick one. */
|
|
69
|
+
brow?: OneOrMany<string>;
|
|
70
|
+
/** Background, 1..5. Array = pick one. */
|
|
71
|
+
bg?: OneOrMany<number | string>;
|
|
72
|
+
/** Eye coloring mode. Array = pick one. Invalid combos fall back to "mono". */
|
|
73
|
+
mode?: OneOrMany<Mode>;
|
|
74
|
+
/** OKLCH fine-tuning overrides. */
|
|
75
|
+
tuning?: Partial<Tuning>;
|
|
76
|
+
/** Generated-hue variety for seeded picks (default 8). */
|
|
77
|
+
genHues?: number;
|
|
78
|
+
}
|
|
79
|
+
type CharInput = number | string | CharConfig;
|
|
80
|
+
/** A fully-resolved, concrete character spec. */
|
|
81
|
+
interface Resolved {
|
|
82
|
+
color: string;
|
|
83
|
+
light: string;
|
|
84
|
+
dark: string;
|
|
85
|
+
shape: string;
|
|
86
|
+
eyes: EyeKind;
|
|
87
|
+
eye: string;
|
|
88
|
+
brow: string;
|
|
89
|
+
bg: string;
|
|
90
|
+
mode: Mode;
|
|
91
|
+
hue: number;
|
|
92
|
+
tuning: Tuning;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Build a body gradient from a single brand color (hex) or a hue (degrees).
|
|
96
|
+
* Handy for inspecting or reusing what `color: '#hex'` would produce.
|
|
97
|
+
*/
|
|
98
|
+
declare function gradient(input: string | number): {
|
|
99
|
+
light: string;
|
|
100
|
+
dark: string;
|
|
101
|
+
};
|
|
102
|
+
declare function resolve(input: CharInput): Resolved;
|
|
103
|
+
|
|
104
|
+
type AnimName = 'blink' | 'jump' | 'breath' | 'dance';
|
|
105
|
+
/** name = animation to play, or null to stop. */
|
|
106
|
+
interface AnimEvent {
|
|
107
|
+
name: AnimName | null;
|
|
108
|
+
}
|
|
109
|
+
type Listener = (e: AnimEvent) => void;
|
|
110
|
+
interface SvgOptions {
|
|
111
|
+
/** Add a background tile (default false). Omit/false -> transparent. */
|
|
112
|
+
background?: boolean;
|
|
113
|
+
/** Id prefix to keep filters/gradients unique across SVGs in one document. */
|
|
114
|
+
uid?: string;
|
|
115
|
+
}
|
|
116
|
+
declare class Character {
|
|
117
|
+
/** The fully-resolved, concrete spec. */
|
|
118
|
+
readonly config: Resolved;
|
|
119
|
+
/** Stable id prefix derived from the spec — keeps .svg() deterministic. */
|
|
120
|
+
private readonly _uid;
|
|
121
|
+
private _listeners;
|
|
122
|
+
constructor(input?: CharInput);
|
|
123
|
+
/** Render this character to a standalone SVG string. */
|
|
124
|
+
svg(opts?: SvgOptions): string;
|
|
125
|
+
toString(): string;
|
|
126
|
+
/** Blink once. */
|
|
127
|
+
blink(): this;
|
|
128
|
+
/** Hop once. */
|
|
129
|
+
jump(): this;
|
|
130
|
+
/** Breathe (loops until stop or another animation). */
|
|
131
|
+
breath(): this;
|
|
132
|
+
/** Dance (loops until stop or another animation). */
|
|
133
|
+
dance(): this;
|
|
134
|
+
/** Stop any running animation. */
|
|
135
|
+
stop(): this;
|
|
136
|
+
/** @internal — used by <Picto> to react to animation calls. */
|
|
137
|
+
_subscribe(fn: Listener): () => void;
|
|
138
|
+
private _emit;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export { type AnimEvent as A, BGS as B, COLORS as C, DEFAULT_TUNING as D, type EyeKind as E, type Mode as M, type OneOrMany as O, PREFIXES as P, type Resolved as R, SHAPES as S, type Tuning as T, type AnimName as a, type CharConfig as b, type CharInput as c, Character as d, type ComposeArgs as e, type PalEntry as f, type SvgOptions as g, buildPalette as h, compose as i, gradient as j, resolve as r };
|