@blobz/avatar-core 0.0.0-stage → 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/README.md +69 -2
- package/dist/avatar.d.ts +137 -0
- package/dist/avatar.js +702 -0
- package/dist/avatar.js.map +1 -0
- package/package.json +46 -4
package/README.md
CHANGED
|
@@ -1,3 +1,70 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @blobz/avatar-core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The framework-free, deterministic SVG engine behind Blobz. The same normalized
|
|
4
|
+
name and palette always produce the same avatar in Node.js, Bun, and the browser.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { createAvatar } from "@blobz/avatar-core";
|
|
10
|
+
|
|
11
|
+
const avatar = createAvatar({
|
|
12
|
+
name: "Ada Lovelace",
|
|
13
|
+
palette: ["#f2f1f4", "#7c6cf2"],
|
|
14
|
+
frame: "rounded",
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
avatar.svg; // Complete 80×80 accessible SVG markup
|
|
18
|
+
avatar.choices; // Numeric choices that can be saved or edited
|
|
19
|
+
avatar.features; // Human-readable choices for a UI
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
For compatibility with the original `index.html` generator, `makeAvatar` keeps
|
|
23
|
+
the same positional API:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { makeAvatar } from "@blobz/avatar-core";
|
|
27
|
+
|
|
28
|
+
const avatar = makeAvatar(
|
|
29
|
+
"Ada Lovelace",
|
|
30
|
+
["#f2f1f4", "#7c6cf2"],
|
|
31
|
+
"circle"
|
|
32
|
+
);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Build a face manually
|
|
36
|
+
|
|
37
|
+
`createCustomAvatar` renders semantic choices directly. It does not use a name
|
|
38
|
+
or a name-derived hash to select missing features, and it validates every
|
|
39
|
+
choice at runtime.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
import { createCustomAvatar } from "@blobz/avatar-core";
|
|
43
|
+
|
|
44
|
+
const avatar = createCustomAvatar({
|
|
45
|
+
palette: ["#ffffff", "#12b76a"],
|
|
46
|
+
frame: "circle",
|
|
47
|
+
choices: {
|
|
48
|
+
shape: "leaf",
|
|
49
|
+
eyes: "distorted",
|
|
50
|
+
look: "centre",
|
|
51
|
+
eyeSpacing: "medium",
|
|
52
|
+
mouth: "wide smile",
|
|
53
|
+
brows: "quizzical",
|
|
54
|
+
mirrored: false,
|
|
55
|
+
faceAngle: 2,
|
|
56
|
+
bodyTilt: -4,
|
|
57
|
+
facePosition: { x: 1, y: 0 },
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
`toCustomAvatarChoices` converts a generated result into the semantic format,
|
|
63
|
+
which lets an editor use a generated face as its custom starting point. The
|
|
64
|
+
legacy `choices` option on `createAvatar` remains available for partial numeric
|
|
65
|
+
overrides.
|
|
66
|
+
|
|
67
|
+
The current feature space contains `4,911,637,500` combinations before palette
|
|
68
|
+
variation. It includes 21 silhouettes, 14 eye styles, 11 mouths, nine brow
|
|
69
|
+
styles, and 25 face angles. The generator draw order is intentionally stable;
|
|
70
|
+
changing it would change existing users' avatars.
|
package/dist/avatar.d.ts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
declare const AVATAR_CHOICES: {
|
|
2
|
+
readonly brows: readonly ["none", "raised", "angry", "worried", "flat", "quizzical", "high", "tilted", "unibrow"];
|
|
3
|
+
readonly eyes: readonly ["big", "dots", "sleepy", "happy", "wink", "squint", "distorted", "grumpy", "uneven", "oval", "side-eye", "crossed", "shocked", "peep wink"];
|
|
4
|
+
readonly gap: readonly ["close", "medium", "wide"];
|
|
5
|
+
readonly look: readonly ["centre", "left", "right", "up", "down"];
|
|
6
|
+
readonly mouth: readonly ["smile", "flat", "open", "frown", "oh", "smirk", "tongue out", "grin", "buck teeth", "wobbly", "wide smile"];
|
|
7
|
+
readonly shape: readonly ["eared", "lopsided", "pillow", "star", "ghost", "pebble", "squircle", "pentagon", "flower", "clover", "sun", "fuzz", "sparkle", "pill", "quarter", "round", "leaf", "soft triangle", "cloud", "drop", "bean"];
|
|
8
|
+
};
|
|
9
|
+
declare const CHOICE_LABELS: {
|
|
10
|
+
readonly brows: readonly ["none", "raised", "angry", "worried", "flat", "quizzical", "high", "tilted", "unibrow"];
|
|
11
|
+
readonly eyes: readonly ["big", "dots", "sleepy", "happy", "wink", "squint", "distorted", "grumpy", "uneven", "oval", "side-eye", "crossed", "shocked", "peep wink"];
|
|
12
|
+
readonly gap: readonly ["close", "medium", "wide"];
|
|
13
|
+
readonly look: readonly ["centre", "left", "right", "up", "down"];
|
|
14
|
+
readonly mouth: readonly ["smile", "flat", "open", "frown", "oh", "smirk", "tongue out", "grin", "buck teeth", "wobbly", "wide smile"];
|
|
15
|
+
readonly shape: readonly ["eared", "lopsided", "pillow", "star", "ghost", "pebble", "squircle", "pentagon", "flower", "clover", "sun", "fuzz", "sparkle", "pill", "quarter", "round", "leaf", "soft triangle", "cloud", "drop", "bean"];
|
|
16
|
+
};
|
|
17
|
+
declare const AVATAR_COMBINATION_COUNT = 4911637500;
|
|
18
|
+
declare const DEFAULT_AVATAR_PALETTE: readonly ["#f2f1f4", "#7c6cf2"];
|
|
19
|
+
type AvatarFrame = "circle" | "rounded" | "square";
|
|
20
|
+
type AvatarPalette = readonly [background: string, body: string];
|
|
21
|
+
interface AvatarChoiceOverrides {
|
|
22
|
+
readonly browI?: number;
|
|
23
|
+
readonly dxI?: number;
|
|
24
|
+
readonly dyI?: number;
|
|
25
|
+
readonly eyeI?: number;
|
|
26
|
+
readonly faceTiltI?: number;
|
|
27
|
+
readonly flip?: number;
|
|
28
|
+
readonly gapI?: number;
|
|
29
|
+
readonly lookI?: number;
|
|
30
|
+
readonly mouthI?: number;
|
|
31
|
+
readonly shapeI?: number;
|
|
32
|
+
readonly tiltI?: number;
|
|
33
|
+
}
|
|
34
|
+
interface AvatarChoices {
|
|
35
|
+
readonly browI: number;
|
|
36
|
+
readonly dxI: number;
|
|
37
|
+
readonly dyI: number;
|
|
38
|
+
readonly eyeI: number;
|
|
39
|
+
readonly faceTiltI: number;
|
|
40
|
+
readonly flip: number;
|
|
41
|
+
readonly gapI: number;
|
|
42
|
+
readonly lookI: number;
|
|
43
|
+
readonly mouthI: number;
|
|
44
|
+
readonly shapeI: number;
|
|
45
|
+
readonly tiltI: number;
|
|
46
|
+
}
|
|
47
|
+
type AvatarBrow = (typeof AVATAR_CHOICES.brows)[number];
|
|
48
|
+
type AvatarEye = (typeof AVATAR_CHOICES.eyes)[number];
|
|
49
|
+
type AvatarEyeSpacing = (typeof AVATAR_CHOICES.gap)[number];
|
|
50
|
+
type AvatarLook = (typeof AVATAR_CHOICES.look)[number];
|
|
51
|
+
type AvatarMouth = (typeof AVATAR_CHOICES.mouth)[number];
|
|
52
|
+
type AvatarShape = (typeof AVATAR_CHOICES.shape)[number];
|
|
53
|
+
interface CustomAvatarChoices {
|
|
54
|
+
readonly bodyTilt: number;
|
|
55
|
+
readonly brows: AvatarBrow;
|
|
56
|
+
readonly eyeSpacing: AvatarEyeSpacing;
|
|
57
|
+
readonly eyes: AvatarEye;
|
|
58
|
+
readonly faceAngle: number;
|
|
59
|
+
readonly facePosition: {
|
|
60
|
+
readonly x: number;
|
|
61
|
+
readonly y: number;
|
|
62
|
+
};
|
|
63
|
+
readonly look: AvatarLook;
|
|
64
|
+
readonly mirrored: boolean;
|
|
65
|
+
readonly mouth: AvatarMouth;
|
|
66
|
+
readonly shape: AvatarShape;
|
|
67
|
+
}
|
|
68
|
+
interface GeneratedAvatarDefinition {
|
|
69
|
+
readonly frame: AvatarFrame;
|
|
70
|
+
readonly name: string | number;
|
|
71
|
+
readonly palette: AvatarPalette;
|
|
72
|
+
readonly type: "generated";
|
|
73
|
+
readonly version: 1;
|
|
74
|
+
}
|
|
75
|
+
interface CustomAvatarDefinition {
|
|
76
|
+
readonly choices: CustomAvatarChoices;
|
|
77
|
+
readonly frame: AvatarFrame;
|
|
78
|
+
readonly palette: AvatarPalette;
|
|
79
|
+
readonly type: "custom";
|
|
80
|
+
readonly version: 1;
|
|
81
|
+
}
|
|
82
|
+
type AvatarDefinition = GeneratedAvatarDefinition | CustomAvatarDefinition;
|
|
83
|
+
interface AvatarFeature {
|
|
84
|
+
readonly at: number;
|
|
85
|
+
readonly label: string;
|
|
86
|
+
readonly of: number;
|
|
87
|
+
readonly swatch?: string;
|
|
88
|
+
readonly value: string;
|
|
89
|
+
}
|
|
90
|
+
interface AvatarRenderResult {
|
|
91
|
+
readonly choices: AvatarChoices;
|
|
92
|
+
readonly combos: number;
|
|
93
|
+
readonly features: readonly AvatarFeature[];
|
|
94
|
+
readonly key: string;
|
|
95
|
+
readonly svg: string;
|
|
96
|
+
}
|
|
97
|
+
interface AvatarResult extends AvatarRenderResult {
|
|
98
|
+
readonly hash: number;
|
|
99
|
+
}
|
|
100
|
+
interface CustomAvatarResult extends AvatarRenderResult {
|
|
101
|
+
readonly key: "custom avatar";
|
|
102
|
+
}
|
|
103
|
+
interface CreateAvatarOptions {
|
|
104
|
+
readonly choices?: AvatarChoiceOverrides | null;
|
|
105
|
+
readonly frame?: AvatarFrame;
|
|
106
|
+
readonly name: unknown;
|
|
107
|
+
readonly palette?: AvatarPalette;
|
|
108
|
+
}
|
|
109
|
+
interface CreateCustomAvatarOptions {
|
|
110
|
+
readonly choices: CustomAvatarChoices;
|
|
111
|
+
readonly frame?: AvatarFrame;
|
|
112
|
+
readonly palette?: AvatarPalette;
|
|
113
|
+
}
|
|
114
|
+
declare const hashAvatarSeed: (value: string, seed?: number) => number;
|
|
115
|
+
declare const createSeededRandom: (seed: number) => (() => number);
|
|
116
|
+
declare const makeAvatar: (input: unknown, colours: AvatarPalette, frame?: AvatarFrame, overrides?: AvatarChoiceOverrides | null) => AvatarResult;
|
|
117
|
+
declare const DEFAULT_CUSTOM_AVATAR_CHOICES: {
|
|
118
|
+
readonly bodyTilt: 0;
|
|
119
|
+
readonly brows: "none";
|
|
120
|
+
readonly eyeSpacing: "medium";
|
|
121
|
+
readonly eyes: "big";
|
|
122
|
+
readonly faceAngle: 0;
|
|
123
|
+
readonly facePosition: {
|
|
124
|
+
readonly x: 0;
|
|
125
|
+
readonly y: 0;
|
|
126
|
+
};
|
|
127
|
+
readonly look: "centre";
|
|
128
|
+
readonly mirrored: false;
|
|
129
|
+
readonly mouth: "smile";
|
|
130
|
+
readonly shape: "round";
|
|
131
|
+
};
|
|
132
|
+
declare const toAvatarChoices: (choices: CustomAvatarChoices) => AvatarChoices;
|
|
133
|
+
declare const toCustomAvatarChoices: (choices: AvatarChoices) => CustomAvatarChoices;
|
|
134
|
+
declare const createCustomAvatar: ({ choices: customChoices, palette, frame, }: CreateCustomAvatarOptions) => CustomAvatarResult;
|
|
135
|
+
declare const createAvatar: ({ name, palette, frame, choices, }: CreateAvatarOptions) => AvatarResult;
|
|
136
|
+
|
|
137
|
+
export { AVATAR_CHOICES, AVATAR_COMBINATION_COUNT, type AvatarBrow, type AvatarChoiceOverrides, type AvatarChoices, type AvatarDefinition, type AvatarEye, type AvatarEyeSpacing, type AvatarFeature, type AvatarFrame, type AvatarLook, type AvatarMouth, type AvatarPalette, type AvatarRenderResult, type AvatarResult, type AvatarShape, CHOICE_LABELS, type CreateAvatarOptions, type CreateCustomAvatarOptions, type CustomAvatarChoices, type CustomAvatarDefinition, type CustomAvatarResult, DEFAULT_AVATAR_PALETTE, DEFAULT_CUSTOM_AVATAR_CHOICES, type GeneratedAvatarDefinition, createAvatar, createCustomAvatar, createSeededRandom, hashAvatarSeed, makeAvatar, toAvatarChoices, toCustomAvatarChoices };
|
package/dist/avatar.js
ADDED
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
// src/avatar.ts
|
|
2
|
+
var AVATAR_CHOICES = {
|
|
3
|
+
brows: [
|
|
4
|
+
"none",
|
|
5
|
+
"raised",
|
|
6
|
+
"angry",
|
|
7
|
+
"worried",
|
|
8
|
+
"flat",
|
|
9
|
+
"quizzical",
|
|
10
|
+
"high",
|
|
11
|
+
"tilted",
|
|
12
|
+
"unibrow"
|
|
13
|
+
],
|
|
14
|
+
eyes: [
|
|
15
|
+
"big",
|
|
16
|
+
"dots",
|
|
17
|
+
"sleepy",
|
|
18
|
+
"happy",
|
|
19
|
+
"wink",
|
|
20
|
+
"squint",
|
|
21
|
+
"distorted",
|
|
22
|
+
"grumpy",
|
|
23
|
+
"uneven",
|
|
24
|
+
"oval",
|
|
25
|
+
"side-eye",
|
|
26
|
+
"crossed",
|
|
27
|
+
"shocked",
|
|
28
|
+
"peep wink"
|
|
29
|
+
],
|
|
30
|
+
gap: ["close", "medium", "wide"],
|
|
31
|
+
look: ["centre", "left", "right", "up", "down"],
|
|
32
|
+
mouth: [
|
|
33
|
+
"smile",
|
|
34
|
+
"flat",
|
|
35
|
+
"open",
|
|
36
|
+
"frown",
|
|
37
|
+
"oh",
|
|
38
|
+
"smirk",
|
|
39
|
+
"tongue out",
|
|
40
|
+
"grin",
|
|
41
|
+
"buck teeth",
|
|
42
|
+
"wobbly",
|
|
43
|
+
"wide smile"
|
|
44
|
+
],
|
|
45
|
+
shape: [
|
|
46
|
+
"eared",
|
|
47
|
+
"lopsided",
|
|
48
|
+
"pillow",
|
|
49
|
+
"star",
|
|
50
|
+
"ghost",
|
|
51
|
+
"pebble",
|
|
52
|
+
"squircle",
|
|
53
|
+
"pentagon",
|
|
54
|
+
"flower",
|
|
55
|
+
"clover",
|
|
56
|
+
"sun",
|
|
57
|
+
"fuzz",
|
|
58
|
+
"sparkle",
|
|
59
|
+
"pill",
|
|
60
|
+
"quarter",
|
|
61
|
+
"round",
|
|
62
|
+
"leaf",
|
|
63
|
+
"soft triangle",
|
|
64
|
+
"cloud",
|
|
65
|
+
"drop",
|
|
66
|
+
"bean"
|
|
67
|
+
]
|
|
68
|
+
};
|
|
69
|
+
var CHOICE_LABELS = AVATAR_CHOICES;
|
|
70
|
+
var AVATAR_COMBINATION_COUNT = 4911637500;
|
|
71
|
+
var DEFAULT_AVATAR_PALETTE = ["#f2f1f4", "#7c6cf2"];
|
|
72
|
+
var XML_ENTITIES = {
|
|
73
|
+
'"': """,
|
|
74
|
+
"&": "&",
|
|
75
|
+
"<": "<",
|
|
76
|
+
">": ">"
|
|
77
|
+
};
|
|
78
|
+
var FRAME_RADII = {
|
|
79
|
+
circle: 40,
|
|
80
|
+
rounded: 18,
|
|
81
|
+
square: 0
|
|
82
|
+
};
|
|
83
|
+
var GLINTS = [
|
|
84
|
+
[
|
|
85
|
+
"M15.4 31.8C15.8 31.1 16.7 28.8 17.6 27.4C18.4 26.0 19.4 24.5 20.6 23.4C21.7 22.2 23.9 21.0 24.6 20.5",
|
|
86
|
+
"M14.0 36.1L13.4 39.1"
|
|
87
|
+
],
|
|
88
|
+
[
|
|
89
|
+
"M15.4 37.4C15.6 36.7 16.1 34.6 16.6 33.3C17.1 31.9 17.7 30.6 18.4 29.4C19.1 28.1 20.4 26.4 20.8 25.8",
|
|
90
|
+
"M14.7 41.9L14.8 44.9"
|
|
91
|
+
],
|
|
92
|
+
[
|
|
93
|
+
"M19.4 23.1L19.7 21.8L20.5 20.5L21.4 19.9L23.1 19.4L24.4 19.6L25.9 20.1L27.3 20.6L28.4 20.9",
|
|
94
|
+
"M20.7 27.7L21.3 30.3"
|
|
95
|
+
],
|
|
96
|
+
["M33.7 25.7L37.7 18.9", "M21.1 32.1L24.1 31.5"],
|
|
97
|
+
[
|
|
98
|
+
"M18.9 36.4C19.0 35.7 19.2 33.5 19.5 32.1C19.9 30.7 20.3 29.3 21.0 28.1C21.6 26.8 22.9 25.0 23.3 24.4",
|
|
99
|
+
"M18.9 40.9L18.9 44.0"
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
"M17.2 41.6C17.3 41.1 17.6 39.2 18.2 38.2C18.7 37.1 19.5 36.2 20.4 35.4C21.3 34.6 23.0 33.7 23.5 33.4",
|
|
103
|
+
"M17.6 46.1L18.7 49.0"
|
|
104
|
+
],
|
|
105
|
+
[
|
|
106
|
+
"M16.9 34.9C16.9 34.1 17.0 32.0 17.2 30.6C17.3 29.1 17.4 27.7 17.8 26.3C18.1 24.9 19.0 23.0 19.2 22.3",
|
|
107
|
+
"M16.9 39.4L17.0 42.5"
|
|
108
|
+
],
|
|
109
|
+
[
|
|
110
|
+
"M19.7 40.9L18.6 37.6A2.5 2.5 0 0 1 19.5 34.8L24.8 31.0",
|
|
111
|
+
"M27.6 28.9L29.7 27.4"
|
|
112
|
+
],
|
|
113
|
+
["M24.7 25.8A7 7 0 0 1 29.3 20.6", "M15.5 39.3A8 8 0 0 1 16.4 37.5"],
|
|
114
|
+
["M18.6 27.8A8.5 8.5 0 0 1 23.3 21.4", "M18.9 31.6A8.5 8.5 0 0 1 18.6 30.2"],
|
|
115
|
+
["M25.0 31.6L24.2 24.8", "M18.3 36.4L21.1 35.2"],
|
|
116
|
+
["M21.9 32.8Q24.0 27.4 29.1 24.4", "M20.5 37.2L20.0 39.0"],
|
|
117
|
+
["M33.6 30.8L36.7 23.4", "M19.3 39.5L23.1 38.0"],
|
|
118
|
+
[
|
|
119
|
+
"M12.2 34.5C12.5 33.9 13.2 31.8 14.0 30.7C14.8 29.5 15.8 28.4 17.0 27.6C18.1 26.8 20.2 26.1 20.9 25.8",
|
|
120
|
+
"M11.1 38.9L10.6 42.0"
|
|
121
|
+
],
|
|
122
|
+
["M21.5 37V31Q21.5 22.6 28.5 22.6", "M21.5 45V42.5"],
|
|
123
|
+
[
|
|
124
|
+
"M17.5 36.0C17.8 35.3 18.4 33.3 19.1 32.0C19.8 30.7 20.6 29.5 21.5 28.4C22.3 27.3 23.9 25.8 24.4 25.3",
|
|
125
|
+
"M16.7 40.4L16.7 43.5"
|
|
126
|
+
],
|
|
127
|
+
[
|
|
128
|
+
"M23.0 28.1L24.0 26.8L25.2 25.7L26.0 24.8L27.2 23.7L28.2 23.0L29.5 22.0L30.8 21.1L31.9 20.5",
|
|
129
|
+
"M20.4 31.7L19.1 34.2"
|
|
130
|
+
],
|
|
131
|
+
["M20.2 44.6L26.2 33.2", "M18.1 48.6L16.6 51.4"],
|
|
132
|
+
[
|
|
133
|
+
"M18.7 31.8A12.5 12.5 0 0 1 25.1 23.0",
|
|
134
|
+
"M13.0 44.1A9.5 9.5 0 0 1 13.9 42.0"
|
|
135
|
+
],
|
|
136
|
+
["M21.9 44.1L27.7 32.6", "M21.0 48.5L21.0 51.6"],
|
|
137
|
+
[
|
|
138
|
+
"M17.4 39.0C17.6 38.3 18.1 36.2 18.5 34.8C18.9 33.4 19.4 32.1 20.0 30.8C20.7 29.5 22.0 27.8 22.3 27.2",
|
|
139
|
+
"M16.6 43.4L16.5 46.5"
|
|
140
|
+
]
|
|
141
|
+
];
|
|
142
|
+
var getItem = (items, index) => {
|
|
143
|
+
const item = items[index];
|
|
144
|
+
if (item === void 0) {
|
|
145
|
+
throw new RangeError(
|
|
146
|
+
`Avatar choice ${index} is outside the available range.`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return item;
|
|
150
|
+
};
|
|
151
|
+
var hashAvatarSeed = (value, seed = 0) => {
|
|
152
|
+
let first = 3735928559 ^ seed;
|
|
153
|
+
let second = 1103547991 ^ seed;
|
|
154
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
155
|
+
const character = value.charCodeAt(index);
|
|
156
|
+
first = Math.imul(first ^ character, 2654435761);
|
|
157
|
+
second = Math.imul(second ^ character, 1597334677);
|
|
158
|
+
}
|
|
159
|
+
first = Math.imul(first ^ first >>> 16, 2246822507) ^ Math.imul(second ^ second >>> 13, 3266489909);
|
|
160
|
+
second = Math.imul(second ^ second >>> 16, 2246822507) ^ Math.imul(first ^ first >>> 13, 3266489909);
|
|
161
|
+
return 4294967296 * (2097151 & second) + (first >>> 0);
|
|
162
|
+
};
|
|
163
|
+
var createSeededRandom = (seed) => {
|
|
164
|
+
let state = seed;
|
|
165
|
+
return () => {
|
|
166
|
+
state |= 0;
|
|
167
|
+
state = state + 1831565813 | 0;
|
|
168
|
+
let value = Math.imul(state ^ state >>> 15, 1 | state);
|
|
169
|
+
value ^= value + Math.imul(value ^ value >>> 7, 61 | value);
|
|
170
|
+
return ((value ^ value >>> 14) >>> 0) / 4294967296;
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
var toRgb = (hex) => {
|
|
174
|
+
const value = Number.parseInt(hex.slice(1), 16);
|
|
175
|
+
return [value >> 16, value >> 8 & 255, value & 255];
|
|
176
|
+
};
|
|
177
|
+
var mix = (first, second, amount) => {
|
|
178
|
+
const a = toRgb(first);
|
|
179
|
+
const b = toRgb(second);
|
|
180
|
+
const channel = (start, end) => Math.round(start + (end - start) * amount).toString(16).padStart(2, "0");
|
|
181
|
+
return `#${channel(a[0], b[0])}${channel(a[1], b[1])}${channel(a[2], b[2])}`;
|
|
182
|
+
};
|
|
183
|
+
var luminance = (hex) => {
|
|
184
|
+
const [red, green, blue] = toRgb(hex);
|
|
185
|
+
return 0.299 * red + 0.587 * green + 0.114 * blue;
|
|
186
|
+
};
|
|
187
|
+
var escapeXml = (value) => value.replace(/[&<>"]/g, (character) => XML_ENTITIES[character] ?? character);
|
|
188
|
+
var choiceOrFallback = (value, count, fallback) => typeof value === "number" && Number.isInteger(value) && value >= 0 && value < count ? value : fallback;
|
|
189
|
+
var renderAvatar = ({
|
|
190
|
+
choices,
|
|
191
|
+
colours,
|
|
192
|
+
frame,
|
|
193
|
+
id,
|
|
194
|
+
key
|
|
195
|
+
}) => {
|
|
196
|
+
const {
|
|
197
|
+
browI,
|
|
198
|
+
dxI,
|
|
199
|
+
dyI,
|
|
200
|
+
eyeI,
|
|
201
|
+
faceTiltI,
|
|
202
|
+
flip,
|
|
203
|
+
gapI,
|
|
204
|
+
lookI,
|
|
205
|
+
mouthI,
|
|
206
|
+
shapeI,
|
|
207
|
+
tiltI
|
|
208
|
+
} = choices;
|
|
209
|
+
const tilt = (tiltI - 7) * 2;
|
|
210
|
+
const dx = dxI - 2;
|
|
211
|
+
const dy = dyI - 1;
|
|
212
|
+
const faceTilt = faceTiltI - 12;
|
|
213
|
+
const [background, blob] = colours;
|
|
214
|
+
const darkBlob = luminance(blob) < 90;
|
|
215
|
+
const pupil = darkBlob ? "#1b1b1b" : mix(blob, "#0a1030", 0.78);
|
|
216
|
+
const ink = darkBlob ? "#ffffff" : pupil;
|
|
217
|
+
const shapePath = (path, width = 2.5) => `<path d="${path}" fill="${blob}" stroke="${blob}" stroke-width="${width}" stroke-linejoin="round"/>`;
|
|
218
|
+
const polygon = (points, width) => `<polygon points="${points}" fill="${blob}" stroke="${blob}" stroke-width="${width}" stroke-linejoin="round"/>`;
|
|
219
|
+
const burst = (points, outerRadius, innerRadius, width) => polygon(
|
|
220
|
+
Array.from({ length: points * 2 }, (_, index) => {
|
|
221
|
+
const angle = (-90 + index * (180 / points)) * Math.PI / 180;
|
|
222
|
+
const radius = index % 2 ? innerRadius : outerRadius;
|
|
223
|
+
return `${(40 + radius * Math.cos(angle)).toFixed(1)},${(42 + radius * Math.sin(angle)).toFixed(1)}`;
|
|
224
|
+
}).join(" "),
|
|
225
|
+
width
|
|
226
|
+
);
|
|
227
|
+
const lobes = (list) => list.map(
|
|
228
|
+
([x, y, radius]) => `<circle cx="${x}" cy="${y}" r="${radius}" fill="${blob}"/>`
|
|
229
|
+
).join("");
|
|
230
|
+
const bodies = [
|
|
231
|
+
{
|
|
232
|
+
f: [40, 46, 1],
|
|
233
|
+
g: shapePath(
|
|
234
|
+
"M10 46C10 27 21 15 30 17C35 18 36 24 40 24C44 24 45 18 50 17C59 15 70 27 70 46C70 61 56 68 40 68C24 68 10 61 10 46Z"
|
|
235
|
+
)
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
f: [40, 44, 1],
|
|
239
|
+
g: shapePath(
|
|
240
|
+
"M12 40C14 24 26 14 36 18C40 20 42 26 48 24C60 20 70 30 68 46C66 62 50 68 36 66C22 64 10 54 12 40Z"
|
|
241
|
+
)
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
f: [40, 40, 0.95],
|
|
245
|
+
g: shapePath(
|
|
246
|
+
"M20 20Q40 29 60 20Q51 40 60 60Q40 51 20 60Q29 40 20 20Z",
|
|
247
|
+
12
|
|
248
|
+
)
|
|
249
|
+
},
|
|
250
|
+
{ f: [40, 45, 0.78], g: burst(5, 27, 15, 8) },
|
|
251
|
+
{
|
|
252
|
+
f: [40, 42, 1],
|
|
253
|
+
g: shapePath(
|
|
254
|
+
"M16 66V38C16 22 28 14 40 14C52 14 64 22 64 38V66Q58 62 52 66T40 66T28 66T16 66Z"
|
|
255
|
+
)
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
f: [41, 48, 1],
|
|
259
|
+
g: shapePath(
|
|
260
|
+
"M16 50C10 38 20 28 30 30C36 31 38 22 46 22C58 22 70 36 68 50C66 62 56 68 42 68C30 68 20 62 16 50Z"
|
|
261
|
+
)
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
f: [40, 41, 1],
|
|
265
|
+
g: shapePath(
|
|
266
|
+
"M14 38C14 18 18 14 38 14C60 14 67 18 67 40C67 62 60 67 40 67C20 67 14 60 14 38Z"
|
|
267
|
+
)
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
f: [40, 43, 0.95],
|
|
271
|
+
g: polygon("40,23 59,36.8 51.8,59.2 28.2,59.2 21,36.8", 14)
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
f: [40, 42, 0.95],
|
|
275
|
+
g: lobes([
|
|
276
|
+
[57, 42, 12],
|
|
277
|
+
[48.5, 56.7, 11],
|
|
278
|
+
[31.5, 56.7, 13],
|
|
279
|
+
[23, 42, 12],
|
|
280
|
+
[31.5, 27.3, 11],
|
|
281
|
+
[48.5, 27.3, 13],
|
|
282
|
+
[40, 42, 19]
|
|
283
|
+
])
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
f: [40, 42, 0.9],
|
|
287
|
+
g: lobes([
|
|
288
|
+
[27, 29, 13],
|
|
289
|
+
[53, 29, 14],
|
|
290
|
+
[27, 55, 14],
|
|
291
|
+
[53, 55, 13],
|
|
292
|
+
[40, 42, 17]
|
|
293
|
+
])
|
|
294
|
+
},
|
|
295
|
+
{ f: [40, 42, 0.95], g: burst(9, 25, 17, 7) },
|
|
296
|
+
{ f: [40, 42, 0.98], g: burst(16, 26, 22.5, 4) },
|
|
297
|
+
{ f: [40, 42, 0.68], g: burst(4, 24, 10, 12) },
|
|
298
|
+
{
|
|
299
|
+
f: [40, 42, 1],
|
|
300
|
+
g: shapePath(
|
|
301
|
+
"M24 22C34 20 46 24 56 22C68 20 74 30 72 42C70 56 62 64 52 62C42 60 30 64 22 62C12 60 6 50 8 40C10 28 14 24 24 22Z"
|
|
302
|
+
)
|
|
303
|
+
},
|
|
304
|
+
{ f: [38, 44, 1], g: shapePath("M21 61V20A41 41 0 0 1 62 61Z", 10) },
|
|
305
|
+
{
|
|
306
|
+
f: [40, 42, 1],
|
|
307
|
+
g: `<ellipse cx="40" cy="42" rx="27.5" ry="26.5" fill="${blob}"/>`
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
f: [40, 41, 0.9],
|
|
311
|
+
g: shapePath("M19 61C8 38 30 8 61 19C72 50 44 72 19 61Z", 9)
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
f: [40, 50, 0.88],
|
|
315
|
+
g: shapePath(
|
|
316
|
+
"M40 13C47 13 51 22 58 35L68 54C73 64 64 69 53 69H27C16 69 7 64 12 54L22 35C29 22 33 13 40 13Z"
|
|
317
|
+
)
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
f: [40, 46, 0.92],
|
|
321
|
+
g: lobes([
|
|
322
|
+
[22, 47, 14],
|
|
323
|
+
[31, 34, 17],
|
|
324
|
+
[47, 34, 18],
|
|
325
|
+
[59, 47, 14],
|
|
326
|
+
[48, 56, 16],
|
|
327
|
+
[31, 56, 16],
|
|
328
|
+
[40, 46, 20]
|
|
329
|
+
])
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
f: [40, 49, 0.9],
|
|
333
|
+
g: shapePath(
|
|
334
|
+
"M40 12C47 22 62 36 62 50C62 63 52 70 40 70C28 70 18 63 18 50C18 36 33 22 40 12Z"
|
|
335
|
+
)
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
f: [40, 45, 0.96],
|
|
339
|
+
g: shapePath(
|
|
340
|
+
"M15 37C18 21 32 14 45 18C54 21 57 30 64 36C73 44 68 59 56 65C43 72 23 67 16 55C13 50 13 43 15 37Z"
|
|
341
|
+
)
|
|
342
|
+
}
|
|
343
|
+
];
|
|
344
|
+
const body = getItem(bodies, shapeI);
|
|
345
|
+
const bodySvg = body.g;
|
|
346
|
+
const glint = getItem(GLINTS, shapeI);
|
|
347
|
+
const whiteBody = bodySvg.split(blob).join("#fff");
|
|
348
|
+
const shine = `<g mask="url(#${id}s)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" opacity=".62"><path d="${glint[0]}" stroke-width="2.3"/><path d="${glint[1]}" stroke-width="2"/></g>`;
|
|
349
|
+
const gap = getItem([8.5, 10, 11.5], gapI);
|
|
350
|
+
const leftX = -gap;
|
|
351
|
+
const rightX = gap;
|
|
352
|
+
const look = getItem(
|
|
353
|
+
[
|
|
354
|
+
[0, 0],
|
|
355
|
+
[-1.6, 0],
|
|
356
|
+
[1.6, 0],
|
|
357
|
+
[0, -1.6],
|
|
358
|
+
[0, 1.6]
|
|
359
|
+
],
|
|
360
|
+
lookI
|
|
361
|
+
);
|
|
362
|
+
const stroke = (width) => `fill="none" stroke="${ink}" stroke-width="${width}" stroke-linecap="round" stroke-linejoin="round"`;
|
|
363
|
+
const big = (x, y = -2, radius = 7.2) => `<circle cx="${x}" cy="${y}" r="${radius}" fill="#fff"/><circle cx="${x + look[0]}" cy="${y + look[1]}" r="${radius * 0.6}" fill="${pupil}"/>`;
|
|
364
|
+
const happy = (x) => `<path d="M${x - 4.5} 0Q${x} -8 ${x + 4.5} 0" ${stroke(2.4)}/>`;
|
|
365
|
+
const sleepy = (x) => `<path d="M${x - 4.5} -4Q${x} 3 ${x + 4.5} -4" ${stroke(2.4)}/>`;
|
|
366
|
+
const grumpy = (x, side) => {
|
|
367
|
+
const first = [x - 9 * side, -5.5];
|
|
368
|
+
const second = [x + 9 * side, 0.5];
|
|
369
|
+
const clipId = `${id}${side > 0 ? "a" : "b"}`;
|
|
370
|
+
return `<clipPath id="${clipId}"><polygon points="${first} ${second} ${x + 9 * side},9 ${x - 9 * side},9"/></clipPath><g clip-path="url(#${clipId})"><circle cx="${x}" cy="-2" r="7.2" fill="#fff"/><circle cx="${x + look[0]}" cy="${1 + look[1]}" r="4.2" fill="${pupil}"/></g><path d="M${x - 4.8 * side} -4.1L${x + 4.8 * side} -0.9" ${stroke(2)}/>`;
|
|
371
|
+
};
|
|
372
|
+
const oval = (x) => `<ellipse cx="${x}" cy="-2" rx="3.7" ry="4.7" fill="${ink}"/><circle cx="${x + 1.2}" cy="-3.6" r="1.1" fill="${darkBlob ? "#1b1b1b" : "#fff"}"/>`;
|
|
373
|
+
const lidded = (x, clipId) => `<clipPath id="${clipId}"><polygon points="${x - 9},-3.5 ${x + 9},-3.5 ${x + 9},9 ${x - 9},9"/></clipPath><g clip-path="url(#${clipId})"><circle cx="${x}" cy="-2" r="7.2" fill="#fff"/><circle cx="${x + look[0] * 1.5}" cy="${1 + look[1] * 0.5}" r="3.8" fill="${pupil}"/></g><path d="M${x - 6.4} -3.5H${x + 6.4}" ${stroke(2.2)}/>`;
|
|
374
|
+
const cross = (x, side) => `<circle cx="${x}" cy="-2" r="7.2" fill="#fff"/><circle cx="${x + 3 * side}" cy="-1.5" r="3.6" fill="${pupil}"/>`;
|
|
375
|
+
const shocked = (x) => `<circle cx="${x}" cy="-2" r="7.2" fill="#fff"/><circle cx="${x + look[0] * 0.8}" cy="${-2 + look[1] * 0.8}" r="1.9" fill="${pupil}"/>`;
|
|
376
|
+
const peepWink = `<circle cx="${leftX}" cy="-2.5" r="4.8" fill="#fff"/><circle cx="${leftX + look[0] * 0.45}" cy="${-2.5 + look[1] * 0.45}" r="2.45" fill="${pupil}"/><path d="M${rightX - 4.2} -1.4Q${rightX} -7.2 ${rightX + 4.4} -1.8" ${stroke(2.1)}/>`;
|
|
377
|
+
const distortedEye = (x, side) => {
|
|
378
|
+
const eyeX = x - side * 0.9;
|
|
379
|
+
const centerY = -3.4;
|
|
380
|
+
const pupilX = eyeX - side * 1.8 + look[0] * 1.2;
|
|
381
|
+
const pupilY = centerY - 0.6 + look[1] * 1.2;
|
|
382
|
+
return `<path transform="translate(${eyeX} ${centerY}) scale(${1.04 * side} 1.04)" d="M-7.5 2.9C-7.6 -3.5 -4.2 -8.8 -0.5 -9.2C2.8 -9.5 6.2 -8.2 7 -5.6C7.9 -2.8 7.6 1.2 5.8 4.8C4 8.2 0.8 9.4 -1.2 9.3C-4.8 9.2 -7.3 6.2 -7.5 2.9Z" fill="#fff"/><ellipse cx="${pupilX}" cy="${pupilY}" rx="2.6" ry="4.3" fill="${pupil}" transform="rotate(${25 * side} ${pupilX} ${pupilY})"/>`;
|
|
383
|
+
};
|
|
384
|
+
const eyes = getItem(
|
|
385
|
+
[
|
|
386
|
+
big(leftX) + big(rightX),
|
|
387
|
+
`<circle cx="${leftX}" cy="-2" r="3.6" fill="${ink}"/><circle cx="${rightX}" cy="-2" r="3.6" fill="${ink}"/>`,
|
|
388
|
+
sleepy(leftX) + sleepy(rightX),
|
|
389
|
+
happy(leftX) + happy(rightX),
|
|
390
|
+
big(leftX) + happy(rightX),
|
|
391
|
+
`<path d="M${leftX - 4} -6L${leftX + 3} -2L${leftX - 4} 2M${rightX + 4} -6L${rightX - 3} -2L${rightX + 4} 2" ${stroke(2.4)}/>`,
|
|
392
|
+
distortedEye(leftX, 1) + distortedEye(rightX, -1),
|
|
393
|
+
grumpy(leftX, 1) + grumpy(rightX, -1),
|
|
394
|
+
big(leftX, -3.5, 8) + big(rightX, -0.5, 5.6),
|
|
395
|
+
oval(leftX) + oval(rightX),
|
|
396
|
+
lidded(leftX, `${id}c`) + lidded(rightX, `${id}d`),
|
|
397
|
+
cross(leftX, 1) + cross(rightX, -1),
|
|
398
|
+
shocked(leftX) + shocked(rightX),
|
|
399
|
+
peepWink
|
|
400
|
+
],
|
|
401
|
+
eyeI
|
|
402
|
+
);
|
|
403
|
+
const mouthY = 8;
|
|
404
|
+
const mouthFill = darkBlob ? "#4a4a58" : pupil;
|
|
405
|
+
const openMouth = (width, height, top, withTeeth) => {
|
|
406
|
+
const path = `M${-width} ${top}H${width}C${width} ${top + height * 0.8} ${width * 0.55} ${top + height} 0 ${top + height}C${-width * 0.55} ${top + height} ${-width} ${top + height * 0.8} ${-width} ${top}Z`;
|
|
407
|
+
const clipId = `${id}m`;
|
|
408
|
+
const teeth = withTeeth ? `<rect x="${-width}" y="${top}" width="${width * 2}" height="2.6" fill="#fff"/>` : "";
|
|
409
|
+
return `<clipPath id="${clipId}"><path d="${path}"/></clipPath><path d="${path}" fill="${mouthFill}" stroke="${mouthFill}" stroke-width="1.6" stroke-linejoin="round"/><g clip-path="url(#${clipId})">${teeth}<ellipse cx="0" cy="${top + height}" rx="${width * 0.62}" ry="${height * 0.42}" fill="#ff8fa3"/></g>`;
|
|
410
|
+
};
|
|
411
|
+
const mouth = getItem(
|
|
412
|
+
[
|
|
413
|
+
`<path d="M-3.2 ${mouthY}Q0 ${mouthY + 3.8} 3.2 ${mouthY}" ${stroke(2.2)}/>`,
|
|
414
|
+
`<path d="M-2.6 ${mouthY + 1}H2.6" ${stroke(2.2)}/>`,
|
|
415
|
+
openMouth(4.4, 7.2, 6.2, false),
|
|
416
|
+
`<path d="M-3 ${mouthY + 2}Q0 ${mouthY - 1.6} 3 ${mouthY + 2}" ${stroke(2.2)}/>`,
|
|
417
|
+
`<ellipse cx="0" cy="${mouthY + 1}" rx="2" ry="2.5" fill="${ink}"/>`,
|
|
418
|
+
`<path d="M-3 ${mouthY}Q0.5 ${mouthY + 4} 3.5 ${mouthY - 1}" ${stroke(2.2)}/>`,
|
|
419
|
+
`<path d="M0.4 9.2V13.2a2.4 2.4 0 0 0 4.8 0V8.6Z" fill="#ff7a93"/><path d="M-5.5 7Q0 13 5.5 7" ${stroke(2.2)}/>`,
|
|
420
|
+
openMouth(7, 7.8, 5.6, true),
|
|
421
|
+
`<path d="M-4 7.5H4" ${stroke(2.2)}/><rect x="-2.6" y="8" width="2.4" height="3.2" rx=".7" fill="#fff" stroke="${pupil}" stroke-width=".8"/><rect x=".2" y="8" width="2.4" height="3.2" rx=".7" fill="#fff" stroke="${pupil}" stroke-width=".8"/>`,
|
|
422
|
+
`<path d="M-5 8Q-2.5 5 0 8T5 8" ${stroke(2.2)}/>`,
|
|
423
|
+
`<path d="M-7 5.4Q0 14.6 7 5.4" ${stroke(2.4)}/>`
|
|
424
|
+
],
|
|
425
|
+
mouthI
|
|
426
|
+
);
|
|
427
|
+
const brow = (x, side) => getItem(
|
|
428
|
+
[
|
|
429
|
+
"",
|
|
430
|
+
`<path d="M${x - 4.5} -12Q${x} -16 ${x + 4.5} -12" ${stroke(2.2)}/>`,
|
|
431
|
+
`<path d="M${x - 5 * side} -15.5L${x + 5 * side} -11.5" ${stroke(2.2)}/>`,
|
|
432
|
+
`<path d="M${x - 5 * side} -11.5L${x + 5 * side} -15.5" ${stroke(2.2)}/>`,
|
|
433
|
+
`<path d="M${x - 4.6} -12.6H${x + 4.6}" ${stroke(2.4)}/>`,
|
|
434
|
+
side > 0 ? `<path d="M${x - 4.5} -13Q${x} -18.2 ${x + 4.5} -13" ${stroke(2.2)}/>` : `<path d="M${x - 4.5} -11.8H${x + 4.5}" ${stroke(2.2)}/>`,
|
|
435
|
+
`<path d="M${x - 5} -14.6Q${x} -22.4 ${x + 5} -14.6" ${stroke(2.2)}/>`,
|
|
436
|
+
`<path d="M${x - 4.6} -11.6L${x + 4.6} -14.4" ${stroke(2.2)}/>`,
|
|
437
|
+
side > 0 ? `<path d="M${x - 5} -12.6Q0 -15.8 ${-x + 5} -12.6" ${stroke(2.4)}/>` : ""
|
|
438
|
+
],
|
|
439
|
+
browI
|
|
440
|
+
);
|
|
441
|
+
let browShift = 0;
|
|
442
|
+
if (eyeI === 8) {
|
|
443
|
+
browShift = -2.5;
|
|
444
|
+
} else if (eyeI === 6) {
|
|
445
|
+
browShift = -3;
|
|
446
|
+
}
|
|
447
|
+
const brows = `<g transform="translate(0 ${browShift})">${brow(leftX, 1)}${brow(rightX, -1)}</g>`;
|
|
448
|
+
const face = `<g transform="translate(${body.f[0] + dx} ${body.f[1] + dy}) scale(${body.f[2]}) rotate(${faceTilt})">${brows}${eyes}${mouth}</g>`;
|
|
449
|
+
const frameRadius = FRAME_RADII[frame] ?? 18;
|
|
450
|
+
const flipTransform = flip ? ' transform="translate(80 0) scale(-1 1)"' : "";
|
|
451
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" width="80" height="80" role="img" aria-label="Blob avatar for ${escapeXml(key)}"><defs><clipPath id="${id}"><rect width="80" height="80" rx="${frameRadius}"/></clipPath><mask id="${id}s" maskUnits="userSpaceOnUse" x="0" y="0" width="80" height="80">${whiteBody}</mask></defs><g clip-path="url(#${id})"><rect width="80" height="80" fill="${background}"/><g transform="translate(40 42) rotate(${tilt}) scale(1.15) translate(-40 -42)"><g${flipTransform}>${bodySvg}${shine}</g><g${flipTransform}>${face}</g></g></g></svg>`;
|
|
452
|
+
const features = [
|
|
453
|
+
{
|
|
454
|
+
at: 1,
|
|
455
|
+
label: "Background",
|
|
456
|
+
of: 1,
|
|
457
|
+
swatch: background,
|
|
458
|
+
value: background
|
|
459
|
+
},
|
|
460
|
+
{ at: 1, label: "Body colour", of: 1, swatch: blob, value: blob },
|
|
461
|
+
{
|
|
462
|
+
at: shapeI + 1,
|
|
463
|
+
label: "Silhouette",
|
|
464
|
+
of: 21,
|
|
465
|
+
value: getItem(AVATAR_CHOICES.shape, shapeI)
|
|
466
|
+
},
|
|
467
|
+
{ at: flip + 1, label: "Mirrored", of: 2, value: flip ? "yes" : "no" },
|
|
468
|
+
{
|
|
469
|
+
at: eyeI + 1,
|
|
470
|
+
label: "Eyes",
|
|
471
|
+
of: 14,
|
|
472
|
+
value: getItem(AVATAR_CHOICES.eyes, eyeI)
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
at: lookI + 1,
|
|
476
|
+
label: "Looking",
|
|
477
|
+
of: 5,
|
|
478
|
+
value: getItem(AVATAR_CHOICES.look, lookI)
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
at: gapI + 1,
|
|
482
|
+
label: "Eye gap",
|
|
483
|
+
of: 3,
|
|
484
|
+
value: getItem(AVATAR_CHOICES.gap, gapI)
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
at: mouthI + 1,
|
|
488
|
+
label: "Mouth",
|
|
489
|
+
of: 11,
|
|
490
|
+
value: getItem(AVATAR_CHOICES.mouth, mouthI)
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
at: browI + 1,
|
|
494
|
+
label: "Brows",
|
|
495
|
+
of: 9,
|
|
496
|
+
value: getItem(AVATAR_CHOICES.brows, browI)
|
|
497
|
+
},
|
|
498
|
+
{ at: faceTiltI + 1, label: "Face angle", of: 25, value: `${faceTilt}\xB0` },
|
|
499
|
+
{ at: tiltI + 1, label: "Tilt", of: 15, value: `${tilt}\xB0` },
|
|
500
|
+
{
|
|
501
|
+
at: dxI * 3 + dyI + 1,
|
|
502
|
+
label: "Face shift",
|
|
503
|
+
of: 15,
|
|
504
|
+
value: `${dx}, ${dy}`
|
|
505
|
+
}
|
|
506
|
+
];
|
|
507
|
+
const combos = features.reduce((total, feature) => total * feature.of, 1);
|
|
508
|
+
return { choices, combos, features, key, svg };
|
|
509
|
+
};
|
|
510
|
+
var makeAvatar = (input, colours, frame = "rounded", overrides = null) => {
|
|
511
|
+
const key = String(input).trim().toLowerCase();
|
|
512
|
+
const hash = hashAvatarSeed(key);
|
|
513
|
+
const random = createSeededRandom(
|
|
514
|
+
hash % 4294967296 ^ Math.floor(hash / 4294967296)
|
|
515
|
+
);
|
|
516
|
+
const integer = (count) => Math.floor(random() * count);
|
|
517
|
+
random();
|
|
518
|
+
random();
|
|
519
|
+
let shapeI = integer(21);
|
|
520
|
+
let eyeI = integer(14);
|
|
521
|
+
let lookI = integer(5);
|
|
522
|
+
let mouthI = integer(11);
|
|
523
|
+
let tiltI = integer(15);
|
|
524
|
+
let dxI = integer(5);
|
|
525
|
+
let dyI = integer(3);
|
|
526
|
+
let gapI = integer(3);
|
|
527
|
+
let flip = integer(2);
|
|
528
|
+
let browI = integer(9);
|
|
529
|
+
let faceTiltI = integer(25);
|
|
530
|
+
if (overrides) {
|
|
531
|
+
shapeI = choiceOrFallback(overrides.shapeI, 21, shapeI);
|
|
532
|
+
eyeI = choiceOrFallback(overrides.eyeI, 14, eyeI);
|
|
533
|
+
lookI = choiceOrFallback(overrides.lookI, 5, lookI);
|
|
534
|
+
mouthI = choiceOrFallback(overrides.mouthI, 11, mouthI);
|
|
535
|
+
tiltI = choiceOrFallback(overrides.tiltI, 15, tiltI);
|
|
536
|
+
dxI = choiceOrFallback(overrides.dxI, 5, dxI);
|
|
537
|
+
dyI = choiceOrFallback(overrides.dyI, 3, dyI);
|
|
538
|
+
gapI = choiceOrFallback(overrides.gapI, 3, gapI);
|
|
539
|
+
flip = choiceOrFallback(overrides.flip, 2, flip);
|
|
540
|
+
browI = choiceOrFallback(overrides.browI, 9, browI);
|
|
541
|
+
faceTiltI = choiceOrFallback(overrides.faceTiltI, 25, faceTiltI);
|
|
542
|
+
}
|
|
543
|
+
const choices = {
|
|
544
|
+
browI,
|
|
545
|
+
dxI,
|
|
546
|
+
dyI,
|
|
547
|
+
eyeI,
|
|
548
|
+
faceTiltI,
|
|
549
|
+
flip,
|
|
550
|
+
gapI,
|
|
551
|
+
lookI,
|
|
552
|
+
mouthI,
|
|
553
|
+
shapeI,
|
|
554
|
+
tiltI
|
|
555
|
+
};
|
|
556
|
+
const id = `fp${hash.toString(36)}-${shapeI}-${eyeI}-${mouthI}-${gapI}${frame.charAt(0)}`;
|
|
557
|
+
const avatar = renderAvatar({ choices, colours, frame, id, key });
|
|
558
|
+
return { ...avatar, hash, key };
|
|
559
|
+
};
|
|
560
|
+
var DEFAULT_CUSTOM_AVATAR_CHOICES = {
|
|
561
|
+
bodyTilt: 0,
|
|
562
|
+
brows: "none",
|
|
563
|
+
eyeSpacing: "medium",
|
|
564
|
+
eyes: "big",
|
|
565
|
+
faceAngle: 0,
|
|
566
|
+
facePosition: { x: 0, y: 0 },
|
|
567
|
+
look: "centre",
|
|
568
|
+
mirrored: false,
|
|
569
|
+
mouth: "smile",
|
|
570
|
+
shape: "round"
|
|
571
|
+
};
|
|
572
|
+
var getChoiceIndex = (choices, value, label) => {
|
|
573
|
+
const index = choices.indexOf(value);
|
|
574
|
+
if (index === -1) {
|
|
575
|
+
throw new RangeError(`Unknown avatar ${label}: ${String(value)}`);
|
|
576
|
+
}
|
|
577
|
+
return index;
|
|
578
|
+
};
|
|
579
|
+
var requireIntegerInRange = (value, minimum, maximum, label) => {
|
|
580
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < minimum || value > maximum) {
|
|
581
|
+
throw new RangeError(
|
|
582
|
+
`${label} must be an integer from ${minimum} to ${maximum}.`
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
return value;
|
|
586
|
+
};
|
|
587
|
+
var requireBoolean = (value, label) => {
|
|
588
|
+
if (typeof value !== "boolean") {
|
|
589
|
+
throw new TypeError(`${label} must be a boolean.`);
|
|
590
|
+
}
|
|
591
|
+
return value;
|
|
592
|
+
};
|
|
593
|
+
var toAvatarChoices = (choices) => {
|
|
594
|
+
const bodyTilt = requireIntegerInRange(
|
|
595
|
+
choices.bodyTilt,
|
|
596
|
+
-14,
|
|
597
|
+
14,
|
|
598
|
+
"Body tilt"
|
|
599
|
+
);
|
|
600
|
+
if (bodyTilt % 2 !== 0) {
|
|
601
|
+
throw new RangeError("Body tilt must use two-degree increments.");
|
|
602
|
+
}
|
|
603
|
+
const faceAngle = requireIntegerInRange(
|
|
604
|
+
choices.faceAngle,
|
|
605
|
+
-12,
|
|
606
|
+
12,
|
|
607
|
+
"Face angle"
|
|
608
|
+
);
|
|
609
|
+
const positionX = requireIntegerInRange(
|
|
610
|
+
choices.facePosition.x,
|
|
611
|
+
-2,
|
|
612
|
+
2,
|
|
613
|
+
"Horizontal face position"
|
|
614
|
+
);
|
|
615
|
+
const positionY = requireIntegerInRange(
|
|
616
|
+
choices.facePosition.y,
|
|
617
|
+
-1,
|
|
618
|
+
1,
|
|
619
|
+
"Vertical face position"
|
|
620
|
+
);
|
|
621
|
+
return {
|
|
622
|
+
browI: getChoiceIndex(AVATAR_CHOICES.brows, choices.brows, "brows"),
|
|
623
|
+
dxI: positionX + 2,
|
|
624
|
+
dyI: positionY + 1,
|
|
625
|
+
eyeI: getChoiceIndex(AVATAR_CHOICES.eyes, choices.eyes, "eyes"),
|
|
626
|
+
faceTiltI: faceAngle + 12,
|
|
627
|
+
flip: requireBoolean(choices.mirrored, "Mirrored") ? 1 : 0,
|
|
628
|
+
gapI: getChoiceIndex(AVATAR_CHOICES.gap, choices.eyeSpacing, "eye spacing"),
|
|
629
|
+
lookI: getChoiceIndex(AVATAR_CHOICES.look, choices.look, "look"),
|
|
630
|
+
mouthI: getChoiceIndex(AVATAR_CHOICES.mouth, choices.mouth, "mouth"),
|
|
631
|
+
shapeI: getChoiceIndex(AVATAR_CHOICES.shape, choices.shape, "shape"),
|
|
632
|
+
tiltI: bodyTilt / 2 + 7
|
|
633
|
+
};
|
|
634
|
+
};
|
|
635
|
+
var toCustomAvatarChoices = (choices) => ({
|
|
636
|
+
bodyTilt: (choices.tiltI - 7) * 2,
|
|
637
|
+
brows: getItem(AVATAR_CHOICES.brows, choices.browI),
|
|
638
|
+
eyeSpacing: getItem(AVATAR_CHOICES.gap, choices.gapI),
|
|
639
|
+
eyes: getItem(AVATAR_CHOICES.eyes, choices.eyeI),
|
|
640
|
+
faceAngle: choices.faceTiltI - 12,
|
|
641
|
+
facePosition: {
|
|
642
|
+
x: choices.dxI - 2,
|
|
643
|
+
y: choices.dyI - 1
|
|
644
|
+
},
|
|
645
|
+
look: getItem(AVATAR_CHOICES.look, choices.lookI),
|
|
646
|
+
mirrored: choices.flip === 1,
|
|
647
|
+
mouth: getItem(AVATAR_CHOICES.mouth, choices.mouthI),
|
|
648
|
+
shape: getItem(AVATAR_CHOICES.shape, choices.shapeI)
|
|
649
|
+
});
|
|
650
|
+
var createCustomAvatar = ({
|
|
651
|
+
choices: customChoices,
|
|
652
|
+
palette = DEFAULT_AVATAR_PALETTE,
|
|
653
|
+
frame = "rounded"
|
|
654
|
+
}) => {
|
|
655
|
+
const choices = toAvatarChoices(customChoices);
|
|
656
|
+
const signature = [
|
|
657
|
+
choices.shapeI,
|
|
658
|
+
choices.eyeI,
|
|
659
|
+
choices.lookI,
|
|
660
|
+
choices.mouthI,
|
|
661
|
+
choices.tiltI,
|
|
662
|
+
choices.dxI,
|
|
663
|
+
choices.dyI,
|
|
664
|
+
choices.gapI,
|
|
665
|
+
choices.flip,
|
|
666
|
+
choices.browI,
|
|
667
|
+
choices.faceTiltI,
|
|
668
|
+
frame,
|
|
669
|
+
palette[0],
|
|
670
|
+
palette[1]
|
|
671
|
+
].join("|");
|
|
672
|
+
const technicalId = `fpc${hashAvatarSeed(signature).toString(36)}`;
|
|
673
|
+
const avatar = renderAvatar({
|
|
674
|
+
choices,
|
|
675
|
+
colours: palette,
|
|
676
|
+
frame,
|
|
677
|
+
id: technicalId,
|
|
678
|
+
key: "custom avatar"
|
|
679
|
+
});
|
|
680
|
+
return { ...avatar, key: "custom avatar" };
|
|
681
|
+
};
|
|
682
|
+
var createAvatar = ({
|
|
683
|
+
name,
|
|
684
|
+
palette = DEFAULT_AVATAR_PALETTE,
|
|
685
|
+
frame = "rounded",
|
|
686
|
+
choices = null
|
|
687
|
+
}) => makeAvatar(name, palette, frame, choices);
|
|
688
|
+
export {
|
|
689
|
+
AVATAR_CHOICES,
|
|
690
|
+
AVATAR_COMBINATION_COUNT,
|
|
691
|
+
CHOICE_LABELS,
|
|
692
|
+
DEFAULT_AVATAR_PALETTE,
|
|
693
|
+
DEFAULT_CUSTOM_AVATAR_CHOICES,
|
|
694
|
+
createAvatar,
|
|
695
|
+
createCustomAvatar,
|
|
696
|
+
createSeededRandom,
|
|
697
|
+
hashAvatarSeed,
|
|
698
|
+
makeAvatar,
|
|
699
|
+
toAvatarChoices,
|
|
700
|
+
toCustomAvatarChoices
|
|
701
|
+
};
|
|
702
|
+
//# sourceMappingURL=avatar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/avatar.ts"],"sourcesContent":["// biome-ignore-all lint/suspicious/noBitwiseOperators: cyrb53 and mulberry32 intentionally use 32-bit bitwise arithmetic for stable seeded output.\nexport const AVATAR_CHOICES = {\n\tbrows: [\n\t\t\"none\",\n\t\t\"raised\",\n\t\t\"angry\",\n\t\t\"worried\",\n\t\t\"flat\",\n\t\t\"quizzical\",\n\t\t\"high\",\n\t\t\"tilted\",\n\t\t\"unibrow\",\n\t],\n\teyes: [\n\t\t\"big\",\n\t\t\"dots\",\n\t\t\"sleepy\",\n\t\t\"happy\",\n\t\t\"wink\",\n\t\t\"squint\",\n\t\t\"distorted\",\n\t\t\"grumpy\",\n\t\t\"uneven\",\n\t\t\"oval\",\n\t\t\"side-eye\",\n\t\t\"crossed\",\n\t\t\"shocked\",\n\t\t\"peep wink\",\n\t],\n\tgap: [\"close\", \"medium\", \"wide\"],\n\tlook: [\"centre\", \"left\", \"right\", \"up\", \"down\"],\n\tmouth: [\n\t\t\"smile\",\n\t\t\"flat\",\n\t\t\"open\",\n\t\t\"frown\",\n\t\t\"oh\",\n\t\t\"smirk\",\n\t\t\"tongue out\",\n\t\t\"grin\",\n\t\t\"buck teeth\",\n\t\t\"wobbly\",\n\t\t\"wide smile\",\n\t],\n\tshape: [\n\t\t\"eared\",\n\t\t\"lopsided\",\n\t\t\"pillow\",\n\t\t\"star\",\n\t\t\"ghost\",\n\t\t\"pebble\",\n\t\t\"squircle\",\n\t\t\"pentagon\",\n\t\t\"flower\",\n\t\t\"clover\",\n\t\t\"sun\",\n\t\t\"fuzz\",\n\t\t\"sparkle\",\n\t\t\"pill\",\n\t\t\"quarter\",\n\t\t\"round\",\n\t\t\"leaf\",\n\t\t\"soft triangle\",\n\t\t\"cloud\",\n\t\t\"drop\",\n\t\t\"bean\",\n\t],\n} as const;\n\nexport const CHOICE_LABELS = AVATAR_CHOICES;\nexport const AVATAR_COMBINATION_COUNT = 4_911_637_500;\nexport const DEFAULT_AVATAR_PALETTE = [\"#f2f1f4\", \"#7c6cf2\"] as const;\n\nexport type AvatarFrame = \"circle\" | \"rounded\" | \"square\";\nexport type AvatarPalette = readonly [background: string, body: string];\n\nexport interface AvatarChoiceOverrides {\n\treadonly browI?: number;\n\treadonly dxI?: number;\n\treadonly dyI?: number;\n\treadonly eyeI?: number;\n\treadonly faceTiltI?: number;\n\treadonly flip?: number;\n\treadonly gapI?: number;\n\treadonly lookI?: number;\n\treadonly mouthI?: number;\n\treadonly shapeI?: number;\n\treadonly tiltI?: number;\n}\n\nexport interface AvatarChoices {\n\treadonly browI: number;\n\treadonly dxI: number;\n\treadonly dyI: number;\n\treadonly eyeI: number;\n\treadonly faceTiltI: number;\n\treadonly flip: number;\n\treadonly gapI: number;\n\treadonly lookI: number;\n\treadonly mouthI: number;\n\treadonly shapeI: number;\n\treadonly tiltI: number;\n}\n\nexport type AvatarBrow = (typeof AVATAR_CHOICES.brows)[number];\nexport type AvatarEye = (typeof AVATAR_CHOICES.eyes)[number];\nexport type AvatarEyeSpacing = (typeof AVATAR_CHOICES.gap)[number];\nexport type AvatarLook = (typeof AVATAR_CHOICES.look)[number];\nexport type AvatarMouth = (typeof AVATAR_CHOICES.mouth)[number];\nexport type AvatarShape = (typeof AVATAR_CHOICES.shape)[number];\n\nexport interface CustomAvatarChoices {\n\treadonly bodyTilt: number;\n\treadonly brows: AvatarBrow;\n\treadonly eyeSpacing: AvatarEyeSpacing;\n\treadonly eyes: AvatarEye;\n\treadonly faceAngle: number;\n\treadonly facePosition: {\n\t\treadonly x: number;\n\t\treadonly y: number;\n\t};\n\treadonly look: AvatarLook;\n\treadonly mirrored: boolean;\n\treadonly mouth: AvatarMouth;\n\treadonly shape: AvatarShape;\n}\n\nexport interface GeneratedAvatarDefinition {\n\treadonly frame: AvatarFrame;\n\treadonly name: string | number;\n\treadonly palette: AvatarPalette;\n\treadonly type: \"generated\";\n\treadonly version: 1;\n}\n\nexport interface CustomAvatarDefinition {\n\treadonly choices: CustomAvatarChoices;\n\treadonly frame: AvatarFrame;\n\treadonly palette: AvatarPalette;\n\treadonly type: \"custom\";\n\treadonly version: 1;\n}\n\nexport type AvatarDefinition =\n\t| GeneratedAvatarDefinition\n\t| CustomAvatarDefinition;\n\nexport interface AvatarFeature {\n\treadonly at: number;\n\treadonly label: string;\n\treadonly of: number;\n\treadonly swatch?: string;\n\treadonly value: string;\n}\n\nexport interface AvatarRenderResult {\n\treadonly choices: AvatarChoices;\n\treadonly combos: number;\n\treadonly features: readonly AvatarFeature[];\n\treadonly key: string;\n\treadonly svg: string;\n}\n\nexport interface AvatarResult extends AvatarRenderResult {\n\treadonly hash: number;\n}\n\nexport interface CustomAvatarResult extends AvatarRenderResult {\n\treadonly key: \"custom avatar\";\n}\n\nexport interface CreateAvatarOptions {\n\treadonly choices?: AvatarChoiceOverrides | null;\n\treadonly frame?: AvatarFrame;\n\treadonly name: unknown;\n\treadonly palette?: AvatarPalette;\n}\n\nexport interface CreateCustomAvatarOptions {\n\treadonly choices: CustomAvatarChoices;\n\treadonly frame?: AvatarFrame;\n\treadonly palette?: AvatarPalette;\n}\n\ninterface Body {\n\treadonly f: readonly [x: number, y: number, scale: number];\n\treadonly g: string;\n}\ntype Point = readonly [x: number, y: number, radius: number];\ntype Rgb = readonly [red: number, green: number, blue: number];\n\nconst XML_ENTITIES: Readonly<Record<string, string>> = {\n\t'\"': \""\",\n\t\"&\": \"&\",\n\t\"<\": \"<\",\n\t\">\": \">\",\n};\nconst FRAME_RADII: Readonly<Record<AvatarFrame, number>> = {\n\tcircle: 40,\n\trounded: 18,\n\tsquare: 0,\n};\n\nconst GLINTS = [\n\t[\n\t\t\"M15.4 31.8C15.8 31.1 16.7 28.8 17.6 27.4C18.4 26.0 19.4 24.5 20.6 23.4C21.7 22.2 23.9 21.0 24.6 20.5\",\n\t\t\"M14.0 36.1L13.4 39.1\",\n\t],\n\t[\n\t\t\"M15.4 37.4C15.6 36.7 16.1 34.6 16.6 33.3C17.1 31.9 17.7 30.6 18.4 29.4C19.1 28.1 20.4 26.4 20.8 25.8\",\n\t\t\"M14.7 41.9L14.8 44.9\",\n\t],\n\t[\n\t\t\"M19.4 23.1L19.7 21.8L20.5 20.5L21.4 19.9L23.1 19.4L24.4 19.6L25.9 20.1L27.3 20.6L28.4 20.9\",\n\t\t\"M20.7 27.7L21.3 30.3\",\n\t],\n\t[\"M33.7 25.7L37.7 18.9\", \"M21.1 32.1L24.1 31.5\"],\n\t[\n\t\t\"M18.9 36.4C19.0 35.7 19.2 33.5 19.5 32.1C19.9 30.7 20.3 29.3 21.0 28.1C21.6 26.8 22.9 25.0 23.3 24.4\",\n\t\t\"M18.9 40.9L18.9 44.0\",\n\t],\n\t[\n\t\t\"M17.2 41.6C17.3 41.1 17.6 39.2 18.2 38.2C18.7 37.1 19.5 36.2 20.4 35.4C21.3 34.6 23.0 33.7 23.5 33.4\",\n\t\t\"M17.6 46.1L18.7 49.0\",\n\t],\n\t[\n\t\t\"M16.9 34.9C16.9 34.1 17.0 32.0 17.2 30.6C17.3 29.1 17.4 27.7 17.8 26.3C18.1 24.9 19.0 23.0 19.2 22.3\",\n\t\t\"M16.9 39.4L17.0 42.5\",\n\t],\n\t[\n\t\t\"M19.7 40.9L18.6 37.6A2.5 2.5 0 0 1 19.5 34.8L24.8 31.0\",\n\t\t\"M27.6 28.9L29.7 27.4\",\n\t],\n\t[\"M24.7 25.8A7 7 0 0 1 29.3 20.6\", \"M15.5 39.3A8 8 0 0 1 16.4 37.5\"],\n\t[\"M18.6 27.8A8.5 8.5 0 0 1 23.3 21.4\", \"M18.9 31.6A8.5 8.5 0 0 1 18.6 30.2\"],\n\t[\"M25.0 31.6L24.2 24.8\", \"M18.3 36.4L21.1 35.2\"],\n\t[\"M21.9 32.8Q24.0 27.4 29.1 24.4\", \"M20.5 37.2L20.0 39.0\"],\n\t[\"M33.6 30.8L36.7 23.4\", \"M19.3 39.5L23.1 38.0\"],\n\t[\n\t\t\"M12.2 34.5C12.5 33.9 13.2 31.8 14.0 30.7C14.8 29.5 15.8 28.4 17.0 27.6C18.1 26.8 20.2 26.1 20.9 25.8\",\n\t\t\"M11.1 38.9L10.6 42.0\",\n\t],\n\t[\"M21.5 37V31Q21.5 22.6 28.5 22.6\", \"M21.5 45V42.5\"],\n\t[\n\t\t\"M17.5 36.0C17.8 35.3 18.4 33.3 19.1 32.0C19.8 30.7 20.6 29.5 21.5 28.4C22.3 27.3 23.9 25.8 24.4 25.3\",\n\t\t\"M16.7 40.4L16.7 43.5\",\n\t],\n\t[\n\t\t\"M23.0 28.1L24.0 26.8L25.2 25.7L26.0 24.8L27.2 23.7L28.2 23.0L29.5 22.0L30.8 21.1L31.9 20.5\",\n\t\t\"M20.4 31.7L19.1 34.2\",\n\t],\n\t[\"M20.2 44.6L26.2 33.2\", \"M18.1 48.6L16.6 51.4\"],\n\t[\n\t\t\"M18.7 31.8A12.5 12.5 0 0 1 25.1 23.0\",\n\t\t\"M13.0 44.1A9.5 9.5 0 0 1 13.9 42.0\",\n\t],\n\t[\"M21.9 44.1L27.7 32.6\", \"M21.0 48.5L21.0 51.6\"],\n\t[\n\t\t\"M17.4 39.0C17.6 38.3 18.1 36.2 18.5 34.8C18.9 33.4 19.4 32.1 20.0 30.8C20.7 29.5 22.0 27.8 22.3 27.2\",\n\t\t\"M16.6 43.4L16.5 46.5\",\n\t],\n] as const;\n\nconst getItem = <Item>(items: readonly Item[], index: number): Item => {\n\tconst item = items[index];\n\tif (item === undefined) {\n\t\tthrow new RangeError(\n\t\t\t`Avatar choice ${index} is outside the available range.`\n\t\t);\n\t}\n\treturn item;\n};\n\nexport const hashAvatarSeed = (value: string, seed = 0): number => {\n\tlet first = 0xde_ad_be_ef ^ seed;\n\tlet second = 0x41_c6_ce_57 ^ seed;\n\tfor (let index = 0; index < value.length; index += 1) {\n\t\tconst character = value.charCodeAt(index);\n\t\tfirst = Math.imul(first ^ character, 2_654_435_761);\n\t\tsecond = Math.imul(second ^ character, 1_597_334_677);\n\t}\n\tfirst =\n\t\tMath.imul(first ^ (first >>> 16), 2_246_822_507) ^\n\t\tMath.imul(second ^ (second >>> 13), 3_266_489_909);\n\tsecond =\n\t\tMath.imul(second ^ (second >>> 16), 2_246_822_507) ^\n\t\tMath.imul(first ^ (first >>> 13), 3_266_489_909);\n\treturn 4_294_967_296 * (2_097_151 & second) + (first >>> 0);\n};\n\nexport const createSeededRandom = (seed: number): (() => number) => {\n\tlet state = seed;\n\treturn () => {\n\t\tstate |= 0;\n\t\tstate = (state + 0x6d_2b_79_f5) | 0;\n\t\tlet value = Math.imul(state ^ (state >>> 15), 1 | state);\n\t\tvalue ^= value + Math.imul(value ^ (value >>> 7), 61 | value);\n\t\treturn ((value ^ (value >>> 14)) >>> 0) / 4_294_967_296;\n\t};\n};\n\nconst toRgb = (hex: string): Rgb => {\n\tconst value = Number.parseInt(hex.slice(1), 16);\n\treturn [value >> 16, (value >> 8) & 255, value & 255];\n};\nconst mix = (first: string, second: string, amount: number): string => {\n\tconst a = toRgb(first);\n\tconst b = toRgb(second);\n\tconst channel = (start: number, end: number) =>\n\t\tMath.round(start + (end - start) * amount)\n\t\t\t.toString(16)\n\t\t\t.padStart(2, \"0\");\n\treturn `#${channel(a[0], b[0])}${channel(a[1], b[1])}${channel(a[2], b[2])}`;\n};\nconst luminance = (hex: string): number => {\n\tconst [red, green, blue] = toRgb(hex);\n\treturn 0.299 * red + 0.587 * green + 0.114 * blue;\n};\nconst escapeXml = (value: string): string =>\n\tvalue.replace(/[&<>\"]/g, (character) => XML_ENTITIES[character] ?? character);\nconst choiceOrFallback = (\n\tvalue: number | undefined,\n\tcount: number,\n\tfallback: number\n): number =>\n\ttypeof value === \"number\" &&\n\tNumber.isInteger(value) &&\n\tvalue >= 0 &&\n\tvalue < count\n\t\t? value\n\t\t: fallback;\n\ninterface RenderAvatarOptions {\n\treadonly choices: AvatarChoices;\n\treadonly colours: AvatarPalette;\n\treadonly frame: AvatarFrame;\n\treadonly id: string;\n\treadonly key: string;\n}\n\nconst renderAvatar = ({\n\tchoices,\n\tcolours,\n\tframe,\n\tid,\n\tkey,\n}: RenderAvatarOptions): AvatarRenderResult => {\n\tconst {\n\t\tbrowI,\n\t\tdxI,\n\t\tdyI,\n\t\teyeI,\n\t\tfaceTiltI,\n\t\tflip,\n\t\tgapI,\n\t\tlookI,\n\t\tmouthI,\n\t\tshapeI,\n\t\ttiltI,\n\t} = choices;\n\tconst tilt = (tiltI - 7) * 2;\n\tconst dx = dxI - 2;\n\tconst dy = dyI - 1;\n\tconst faceTilt = faceTiltI - 12;\n\tconst [background, blob] = colours;\n\tconst darkBlob = luminance(blob) < 90;\n\tconst pupil = darkBlob ? \"#1b1b1b\" : mix(blob, \"#0a1030\", 0.78);\n\tconst ink = darkBlob ? \"#ffffff\" : pupil;\n\n\tconst shapePath = (path: string, width = 2.5) =>\n\t\t`<path d=\"${path}\" fill=\"${blob}\" stroke=\"${blob}\" stroke-width=\"${width}\" stroke-linejoin=\"round\"/>`;\n\tconst polygon = (points: string, width: number) =>\n\t\t`<polygon points=\"${points}\" fill=\"${blob}\" stroke=\"${blob}\" stroke-width=\"${width}\" stroke-linejoin=\"round\"/>`;\n\tconst burst = (\n\t\tpoints: number,\n\t\touterRadius: number,\n\t\tinnerRadius: number,\n\t\twidth: number\n\t) =>\n\t\tpolygon(\n\t\t\tArray.from({ length: points * 2 }, (_, index) => {\n\t\t\t\tconst angle = ((-90 + index * (180 / points)) * Math.PI) / 180;\n\t\t\t\tconst radius = index % 2 ? innerRadius : outerRadius;\n\t\t\t\treturn `${(40 + radius * Math.cos(angle)).toFixed(1)},${(42 + radius * Math.sin(angle)).toFixed(1)}`;\n\t\t\t}).join(\" \"),\n\t\t\twidth\n\t\t);\n\tconst lobes = (list: readonly Point[]) =>\n\t\tlist\n\t\t\t.map(\n\t\t\t\t([x, y, radius]) =>\n\t\t\t\t\t`<circle cx=\"${x}\" cy=\"${y}\" r=\"${radius}\" fill=\"${blob}\"/>`\n\t\t\t)\n\t\t\t.join(\"\");\n\tconst bodies: readonly Body[] = [\n\t\t{\n\t\t\tf: [40, 46, 1],\n\t\t\tg: shapePath(\n\t\t\t\t\"M10 46C10 27 21 15 30 17C35 18 36 24 40 24C44 24 45 18 50 17C59 15 70 27 70 46C70 61 56 68 40 68C24 68 10 61 10 46Z\"\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tf: [40, 44, 1],\n\t\t\tg: shapePath(\n\t\t\t\t\"M12 40C14 24 26 14 36 18C40 20 42 26 48 24C60 20 70 30 68 46C66 62 50 68 36 66C22 64 10 54 12 40Z\"\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tf: [40, 40, 0.95],\n\t\t\tg: shapePath(\n\t\t\t\t\"M20 20Q40 29 60 20Q51 40 60 60Q40 51 20 60Q29 40 20 20Z\",\n\t\t\t\t12\n\t\t\t),\n\t\t},\n\t\t{ f: [40, 45, 0.78], g: burst(5, 27, 15, 8) },\n\t\t{\n\t\t\tf: [40, 42, 1],\n\t\t\tg: shapePath(\n\t\t\t\t\"M16 66V38C16 22 28 14 40 14C52 14 64 22 64 38V66Q58 62 52 66T40 66T28 66T16 66Z\"\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tf: [41, 48, 1],\n\t\t\tg: shapePath(\n\t\t\t\t\"M16 50C10 38 20 28 30 30C36 31 38 22 46 22C58 22 70 36 68 50C66 62 56 68 42 68C30 68 20 62 16 50Z\"\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tf: [40, 41, 1],\n\t\t\tg: shapePath(\n\t\t\t\t\"M14 38C14 18 18 14 38 14C60 14 67 18 67 40C67 62 60 67 40 67C20 67 14 60 14 38Z\"\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tf: [40, 43, 0.95],\n\t\t\tg: polygon(\"40,23 59,36.8 51.8,59.2 28.2,59.2 21,36.8\", 14),\n\t\t},\n\t\t{\n\t\t\tf: [40, 42, 0.95],\n\t\t\tg: lobes([\n\t\t\t\t[57, 42, 12],\n\t\t\t\t[48.5, 56.7, 11],\n\t\t\t\t[31.5, 56.7, 13],\n\t\t\t\t[23, 42, 12],\n\t\t\t\t[31.5, 27.3, 11],\n\t\t\t\t[48.5, 27.3, 13],\n\t\t\t\t[40, 42, 19],\n\t\t\t]),\n\t\t},\n\t\t{\n\t\t\tf: [40, 42, 0.9],\n\t\t\tg: lobes([\n\t\t\t\t[27, 29, 13],\n\t\t\t\t[53, 29, 14],\n\t\t\t\t[27, 55, 14],\n\t\t\t\t[53, 55, 13],\n\t\t\t\t[40, 42, 17],\n\t\t\t]),\n\t\t},\n\t\t{ f: [40, 42, 0.95], g: burst(9, 25, 17, 7) },\n\t\t{ f: [40, 42, 0.98], g: burst(16, 26, 22.5, 4) },\n\t\t{ f: [40, 42, 0.68], g: burst(4, 24, 10, 12) },\n\t\t{\n\t\t\tf: [40, 42, 1],\n\t\t\tg: shapePath(\n\t\t\t\t\"M24 22C34 20 46 24 56 22C68 20 74 30 72 42C70 56 62 64 52 62C42 60 30 64 22 62C12 60 6 50 8 40C10 28 14 24 24 22Z\"\n\t\t\t),\n\t\t},\n\t\t{ f: [38, 44, 1], g: shapePath(\"M21 61V20A41 41 0 0 1 62 61Z\", 10) },\n\t\t{\n\t\t\tf: [40, 42, 1],\n\t\t\tg: `<ellipse cx=\"40\" cy=\"42\" rx=\"27.5\" ry=\"26.5\" fill=\"${blob}\"/>`,\n\t\t},\n\t\t{\n\t\t\tf: [40, 41, 0.9],\n\t\t\tg: shapePath(\"M19 61C8 38 30 8 61 19C72 50 44 72 19 61Z\", 9),\n\t\t},\n\t\t{\n\t\t\tf: [40, 50, 0.88],\n\t\t\tg: shapePath(\n\t\t\t\t\"M40 13C47 13 51 22 58 35L68 54C73 64 64 69 53 69H27C16 69 7 64 12 54L22 35C29 22 33 13 40 13Z\"\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tf: [40, 46, 0.92],\n\t\t\tg: lobes([\n\t\t\t\t[22, 47, 14],\n\t\t\t\t[31, 34, 17],\n\t\t\t\t[47, 34, 18],\n\t\t\t\t[59, 47, 14],\n\t\t\t\t[48, 56, 16],\n\t\t\t\t[31, 56, 16],\n\t\t\t\t[40, 46, 20],\n\t\t\t]),\n\t\t},\n\t\t{\n\t\t\tf: [40, 49, 0.9],\n\t\t\tg: shapePath(\n\t\t\t\t\"M40 12C47 22 62 36 62 50C62 63 52 70 40 70C28 70 18 63 18 50C18 36 33 22 40 12Z\"\n\t\t\t),\n\t\t},\n\t\t{\n\t\t\tf: [40, 45, 0.96],\n\t\t\tg: shapePath(\n\t\t\t\t\"M15 37C18 21 32 14 45 18C54 21 57 30 64 36C73 44 68 59 56 65C43 72 23 67 16 55C13 50 13 43 15 37Z\"\n\t\t\t),\n\t\t},\n\t];\n\n\tconst body = getItem(bodies, shapeI);\n\tconst bodySvg = body.g;\n\tconst glint = getItem(GLINTS, shapeI);\n\tconst whiteBody = bodySvg.split(blob).join(\"#fff\");\n\tconst shine =\n\t\t`<g mask=\"url(#${id}s)\" fill=\"none\" stroke=\"#fff\" stroke-linecap=\"round\" stroke-linejoin=\"round\" opacity=\".62\">` +\n\t\t`<path d=\"${glint[0]}\" stroke-width=\"2.3\"/>` +\n\t\t`<path d=\"${glint[1]}\" stroke-width=\"2\"/></g>`;\n\n\tconst gap = getItem([8.5, 10, 11.5] as const, gapI);\n\tconst leftX = -gap;\n\tconst rightX = gap;\n\tconst look = getItem(\n\t\t[\n\t\t\t[0, 0],\n\t\t\t[-1.6, 0],\n\t\t\t[1.6, 0],\n\t\t\t[0, -1.6],\n\t\t\t[0, 1.6],\n\t\t] as const,\n\t\tlookI\n\t);\n\tconst stroke = (width: number) =>\n\t\t`fill=\"none\" stroke=\"${ink}\" stroke-width=\"${width}\" stroke-linecap=\"round\" stroke-linejoin=\"round\"`;\n\tconst big = (x: number, y = -2, radius = 7.2) =>\n\t\t`<circle cx=\"${x}\" cy=\"${y}\" r=\"${radius}\" fill=\"#fff\"/><circle cx=\"${x + look[0]}\" cy=\"${y + look[1]}\" r=\"${radius * 0.6}\" fill=\"${pupil}\"/>`;\n\tconst happy = (x: number) =>\n\t\t`<path d=\"M${x - 4.5} 0Q${x} -8 ${x + 4.5} 0\" ${stroke(2.4)}/>`;\n\tconst sleepy = (x: number) =>\n\t\t`<path d=\"M${x - 4.5} -4Q${x} 3 ${x + 4.5} -4\" ${stroke(2.4)}/>`;\n\tconst grumpy = (x: number, side: number) => {\n\t\tconst first = [x - 9 * side, -5.5];\n\t\tconst second = [x + 9 * side, 0.5];\n\t\tconst clipId = `${id}${side > 0 ? \"a\" : \"b\"}`;\n\t\treturn (\n\t\t\t`<clipPath id=\"${clipId}\"><polygon points=\"${first} ${second} ${x + 9 * side},9 ${x - 9 * side},9\"/></clipPath>` +\n\t\t\t`<g clip-path=\"url(#${clipId})\"><circle cx=\"${x}\" cy=\"-2\" r=\"7.2\" fill=\"#fff\"/><circle cx=\"${x + look[0]}\" cy=\"${1 + look[1]}\" r=\"4.2\" fill=\"${pupil}\"/></g>` +\n\t\t\t`<path d=\"M${x - 4.8 * side} -4.1L${x + 4.8 * side} -0.9\" ${stroke(2)}/>`\n\t\t);\n\t};\n\tconst oval = (x: number) =>\n\t\t`<ellipse cx=\"${x}\" cy=\"-2\" rx=\"3.7\" ry=\"4.7\" fill=\"${ink}\"/><circle cx=\"${x + 1.2}\" cy=\"-3.6\" r=\"1.1\" fill=\"${darkBlob ? \"#1b1b1b\" : \"#fff\"}\"/>`;\n\tconst lidded = (x: number, clipId: string) =>\n\t\t`<clipPath id=\"${clipId}\"><polygon points=\"${x - 9},-3.5 ${x + 9},-3.5 ${x + 9},9 ${x - 9},9\"/></clipPath>` +\n\t\t`<g clip-path=\"url(#${clipId})\"><circle cx=\"${x}\" cy=\"-2\" r=\"7.2\" fill=\"#fff\"/><circle cx=\"${x + look[0] * 1.5}\" cy=\"${1 + look[1] * 0.5}\" r=\"3.8\" fill=\"${pupil}\"/></g>` +\n\t\t`<path d=\"M${x - 6.4} -3.5H${x + 6.4}\" ${stroke(2.2)}/>`;\n\tconst cross = (x: number, side: number) =>\n\t\t`<circle cx=\"${x}\" cy=\"-2\" r=\"7.2\" fill=\"#fff\"/><circle cx=\"${x + 3 * side}\" cy=\"-1.5\" r=\"3.6\" fill=\"${pupil}\"/>`;\n\tconst shocked = (x: number) =>\n\t\t`<circle cx=\"${x}\" cy=\"-2\" r=\"7.2\" fill=\"#fff\"/><circle cx=\"${x + look[0] * 0.8}\" cy=\"${-2 + look[1] * 0.8}\" r=\"1.9\" fill=\"${pupil}\"/>`;\n\tconst peepWink =\n\t\t`<circle cx=\"${leftX}\" cy=\"-2.5\" r=\"4.8\" fill=\"#fff\"/>` +\n\t\t`<circle cx=\"${leftX + look[0] * 0.45}\" cy=\"${-2.5 + look[1] * 0.45}\" r=\"2.45\" fill=\"${pupil}\"/>` +\n\t\t`<path d=\"M${rightX - 4.2} -1.4Q${rightX} -7.2 ${rightX + 4.4} -1.8\" ${stroke(2.1)}/>`;\n\tconst distortedEye = (x: number, side: number) => {\n\t\tconst eyeX = x - side * 0.9;\n\t\tconst centerY = -3.4;\n\t\tconst pupilX = eyeX - side * 1.8 + look[0] * 1.2;\n\t\tconst pupilY = centerY - 0.6 + look[1] * 1.2;\n\t\treturn (\n\t\t\t`<path transform=\"translate(${eyeX} ${centerY}) scale(${1.04 * side} 1.04)\" d=\"M-7.5 2.9C-7.6 -3.5 -4.2 -8.8 -0.5 -9.2C2.8 -9.5 6.2 -8.2 7 -5.6C7.9 -2.8 7.6 1.2 5.8 4.8C4 8.2 0.8 9.4 -1.2 9.3C-4.8 9.2 -7.3 6.2 -7.5 2.9Z\" fill=\"#fff\"/>` +\n\t\t\t`<ellipse cx=\"${pupilX}\" cy=\"${pupilY}\" rx=\"2.6\" ry=\"4.3\" fill=\"${pupil}\" transform=\"rotate(${25 * side} ${pupilX} ${pupilY})\"/>`\n\t\t);\n\t};\n\tconst eyes = getItem(\n\t\t[\n\t\t\tbig(leftX) + big(rightX),\n\t\t\t`<circle cx=\"${leftX}\" cy=\"-2\" r=\"3.6\" fill=\"${ink}\"/><circle cx=\"${rightX}\" cy=\"-2\" r=\"3.6\" fill=\"${ink}\"/>`,\n\t\t\tsleepy(leftX) + sleepy(rightX),\n\t\t\thappy(leftX) + happy(rightX),\n\t\t\tbig(leftX) + happy(rightX),\n\t\t\t`<path d=\"M${leftX - 4} -6L${leftX + 3} -2L${leftX - 4} 2M${rightX + 4} -6L${rightX - 3} -2L${rightX + 4} 2\" ${stroke(2.4)}/>`,\n\t\t\tdistortedEye(leftX, 1) + distortedEye(rightX, -1),\n\t\t\tgrumpy(leftX, 1) + grumpy(rightX, -1),\n\t\t\tbig(leftX, -3.5, 8) + big(rightX, -0.5, 5.6),\n\t\t\toval(leftX) + oval(rightX),\n\t\t\tlidded(leftX, `${id}c`) + lidded(rightX, `${id}d`),\n\t\t\tcross(leftX, 1) + cross(rightX, -1),\n\t\t\tshocked(leftX) + shocked(rightX),\n\t\t\tpeepWink,\n\t\t],\n\t\teyeI\n\t);\n\n\tconst mouthY = 8;\n\tconst mouthFill = darkBlob ? \"#4a4a58\" : pupil;\n\tconst openMouth = (\n\t\twidth: number,\n\t\theight: number,\n\t\ttop: number,\n\t\twithTeeth: boolean\n\t) => {\n\t\tconst path = `M${-width} ${top}H${width}C${width} ${top + height * 0.8} ${width * 0.55} ${top + height} 0 ${top + height}C${-width * 0.55} ${top + height} ${-width} ${top + height * 0.8} ${-width} ${top}Z`;\n\t\tconst clipId = `${id}m`;\n\t\tconst teeth = withTeeth\n\t\t\t? `<rect x=\"${-width}\" y=\"${top}\" width=\"${width * 2}\" height=\"2.6\" fill=\"#fff\"/>`\n\t\t\t: \"\";\n\t\treturn (\n\t\t\t`<clipPath id=\"${clipId}\"><path d=\"${path}\"/></clipPath>` +\n\t\t\t`<path d=\"${path}\" fill=\"${mouthFill}\" stroke=\"${mouthFill}\" stroke-width=\"1.6\" stroke-linejoin=\"round\"/>` +\n\t\t\t`<g clip-path=\"url(#${clipId})\">${teeth}` +\n\t\t\t`<ellipse cx=\"0\" cy=\"${top + height}\" rx=\"${width * 0.62}\" ry=\"${height * 0.42}\" fill=\"#ff8fa3\"/></g>`\n\t\t);\n\t};\n\tconst mouth = getItem(\n\t\t[\n\t\t\t`<path d=\"M-3.2 ${mouthY}Q0 ${mouthY + 3.8} 3.2 ${mouthY}\" ${stroke(2.2)}/>`,\n\t\t\t`<path d=\"M-2.6 ${mouthY + 1}H2.6\" ${stroke(2.2)}/>`,\n\t\t\topenMouth(4.4, 7.2, 6.2, false),\n\t\t\t`<path d=\"M-3 ${mouthY + 2}Q0 ${mouthY - 1.6} 3 ${mouthY + 2}\" ${stroke(2.2)}/>`,\n\t\t\t`<ellipse cx=\"0\" cy=\"${mouthY + 1}\" rx=\"2\" ry=\"2.5\" fill=\"${ink}\"/>`,\n\t\t\t`<path d=\"M-3 ${mouthY}Q0.5 ${mouthY + 4} 3.5 ${mouthY - 1}\" ${stroke(2.2)}/>`,\n\t\t\t`<path d=\"M0.4 9.2V13.2a2.4 2.4 0 0 0 4.8 0V8.6Z\" fill=\"#ff7a93\"/><path d=\"M-5.5 7Q0 13 5.5 7\" ${stroke(2.2)}/>`,\n\t\t\topenMouth(7, 7.8, 5.6, true),\n\t\t\t`<path d=\"M-4 7.5H4\" ${stroke(2.2)}/><rect x=\"-2.6\" y=\"8\" width=\"2.4\" height=\"3.2\" rx=\".7\" fill=\"#fff\" stroke=\"${pupil}\" stroke-width=\".8\"/><rect x=\".2\" y=\"8\" width=\"2.4\" height=\"3.2\" rx=\".7\" fill=\"#fff\" stroke=\"${pupil}\" stroke-width=\".8\"/>`,\n\t\t\t`<path d=\"M-5 8Q-2.5 5 0 8T5 8\" ${stroke(2.2)}/>`,\n\t\t\t`<path d=\"M-7 5.4Q0 14.6 7 5.4\" ${stroke(2.4)}/>`,\n\t\t],\n\t\tmouthI\n\t);\n\tconst brow = (x: number, side: number) =>\n\t\tgetItem(\n\t\t\t[\n\t\t\t\t\"\",\n\t\t\t\t`<path d=\"M${x - 4.5} -12Q${x} -16 ${x + 4.5} -12\" ${stroke(2.2)}/>`,\n\t\t\t\t`<path d=\"M${x - 5 * side} -15.5L${x + 5 * side} -11.5\" ${stroke(2.2)}/>`,\n\t\t\t\t`<path d=\"M${x - 5 * side} -11.5L${x + 5 * side} -15.5\" ${stroke(2.2)}/>`,\n\t\t\t\t`<path d=\"M${x - 4.6} -12.6H${x + 4.6}\" ${stroke(2.4)}/>`,\n\t\t\t\tside > 0\n\t\t\t\t\t? `<path d=\"M${x - 4.5} -13Q${x} -18.2 ${x + 4.5} -13\" ${stroke(2.2)}/>`\n\t\t\t\t\t: `<path d=\"M${x - 4.5} -11.8H${x + 4.5}\" ${stroke(2.2)}/>`,\n\t\t\t\t`<path d=\"M${x - 5} -14.6Q${x} -22.4 ${x + 5} -14.6\" ${stroke(2.2)}/>`,\n\t\t\t\t`<path d=\"M${x - 4.6} -11.6L${x + 4.6} -14.4\" ${stroke(2.2)}/>`,\n\t\t\t\tside > 0\n\t\t\t\t\t? `<path d=\"M${x - 5} -12.6Q0 -15.8 ${-x + 5} -12.6\" ${stroke(2.4)}/>`\n\t\t\t\t\t: \"\",\n\t\t\t],\n\t\t\tbrowI\n\t\t);\n\tlet browShift = 0;\n\tif (eyeI === 8) {\n\t\tbrowShift = -2.5;\n\t} else if (eyeI === 6) {\n\t\tbrowShift = -3;\n\t}\n\tconst brows = `<g transform=\"translate(0 ${browShift})\">${brow(leftX, 1)}${brow(rightX, -1)}</g>`;\n\tconst face = `<g transform=\"translate(${body.f[0] + dx} ${body.f[1] + dy}) scale(${body.f[2]}) rotate(${faceTilt})\">${brows}${eyes}${mouth}</g>`;\n\n\tconst frameRadius = FRAME_RADII[frame] ?? 18;\n\tconst flipTransform = flip ? ' transform=\"translate(80 0) scale(-1 1)\"' : \"\";\n\tconst svg =\n\t\t`<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 80 80\" width=\"80\" height=\"80\" role=\"img\" aria-label=\"Blob avatar for ${escapeXml(key)}\">` +\n\t\t`<defs><clipPath id=\"${id}\"><rect width=\"80\" height=\"80\" rx=\"${frameRadius}\"/></clipPath>` +\n\t\t`<mask id=\"${id}s\" maskUnits=\"userSpaceOnUse\" x=\"0\" y=\"0\" width=\"80\" height=\"80\">${whiteBody}</mask></defs>` +\n\t\t`<g clip-path=\"url(#${id})\"><rect width=\"80\" height=\"80\" fill=\"${background}\"/>` +\n\t\t`<g transform=\"translate(40 42) rotate(${tilt}) scale(1.15) translate(-40 -42)\"><g${flipTransform}>${bodySvg}${shine}</g><g${flipTransform}>${face}</g></g></g></svg>`;\n\n\tconst features: readonly AvatarFeature[] = [\n\t\t{\n\t\t\tat: 1,\n\t\t\tlabel: \"Background\",\n\t\t\tof: 1,\n\t\t\tswatch: background,\n\t\t\tvalue: background,\n\t\t},\n\t\t{ at: 1, label: \"Body colour\", of: 1, swatch: blob, value: blob },\n\t\t{\n\t\t\tat: shapeI + 1,\n\t\t\tlabel: \"Silhouette\",\n\t\t\tof: 21,\n\t\t\tvalue: getItem(AVATAR_CHOICES.shape, shapeI),\n\t\t},\n\t\t{ at: flip + 1, label: \"Mirrored\", of: 2, value: flip ? \"yes\" : \"no\" },\n\t\t{\n\t\t\tat: eyeI + 1,\n\t\t\tlabel: \"Eyes\",\n\t\t\tof: 14,\n\t\t\tvalue: getItem(AVATAR_CHOICES.eyes, eyeI),\n\t\t},\n\t\t{\n\t\t\tat: lookI + 1,\n\t\t\tlabel: \"Looking\",\n\t\t\tof: 5,\n\t\t\tvalue: getItem(AVATAR_CHOICES.look, lookI),\n\t\t},\n\t\t{\n\t\t\tat: gapI + 1,\n\t\t\tlabel: \"Eye gap\",\n\t\t\tof: 3,\n\t\t\tvalue: getItem(AVATAR_CHOICES.gap, gapI),\n\t\t},\n\t\t{\n\t\t\tat: mouthI + 1,\n\t\t\tlabel: \"Mouth\",\n\t\t\tof: 11,\n\t\t\tvalue: getItem(AVATAR_CHOICES.mouth, mouthI),\n\t\t},\n\t\t{\n\t\t\tat: browI + 1,\n\t\t\tlabel: \"Brows\",\n\t\t\tof: 9,\n\t\t\tvalue: getItem(AVATAR_CHOICES.brows, browI),\n\t\t},\n\t\t{ at: faceTiltI + 1, label: \"Face angle\", of: 25, value: `${faceTilt}°` },\n\t\t{ at: tiltI + 1, label: \"Tilt\", of: 15, value: `${tilt}°` },\n\t\t{\n\t\t\tat: dxI * 3 + dyI + 1,\n\t\t\tlabel: \"Face shift\",\n\t\t\tof: 15,\n\t\t\tvalue: `${dx}, ${dy}`,\n\t\t},\n\t];\n\tconst combos = features.reduce((total, feature) => total * feature.of, 1);\n\treturn { choices, combos, features, key, svg };\n};\n\nexport const makeAvatar = (\n\tinput: unknown,\n\tcolours: AvatarPalette,\n\tframe: AvatarFrame = \"rounded\",\n\toverrides: AvatarChoiceOverrides | null = null\n): AvatarResult => {\n\tconst key = String(input).trim().toLowerCase();\n\tconst hash = hashAvatarSeed(key);\n\tconst random = createSeededRandom(\n\t\t(hash % 4_294_967_296) ^ Math.floor(hash / 4_294_967_296)\n\t);\n\tconst integer = (count: number) => Math.floor(random() * count);\n\n\trandom();\n\trandom();\n\tlet shapeI = integer(21);\n\tlet eyeI = integer(14);\n\tlet lookI = integer(5);\n\tlet mouthI = integer(11);\n\tlet tiltI = integer(15);\n\tlet dxI = integer(5);\n\tlet dyI = integer(3);\n\tlet gapI = integer(3);\n\tlet flip = integer(2);\n\tlet browI = integer(9);\n\tlet faceTiltI = integer(25);\n\tif (overrides) {\n\t\tshapeI = choiceOrFallback(overrides.shapeI, 21, shapeI);\n\t\teyeI = choiceOrFallback(overrides.eyeI, 14, eyeI);\n\t\tlookI = choiceOrFallback(overrides.lookI, 5, lookI);\n\t\tmouthI = choiceOrFallback(overrides.mouthI, 11, mouthI);\n\t\ttiltI = choiceOrFallback(overrides.tiltI, 15, tiltI);\n\t\tdxI = choiceOrFallback(overrides.dxI, 5, dxI);\n\t\tdyI = choiceOrFallback(overrides.dyI, 3, dyI);\n\t\tgapI = choiceOrFallback(overrides.gapI, 3, gapI);\n\t\tflip = choiceOrFallback(overrides.flip, 2, flip);\n\t\tbrowI = choiceOrFallback(overrides.browI, 9, browI);\n\t\tfaceTiltI = choiceOrFallback(overrides.faceTiltI, 25, faceTiltI);\n\t}\n\tconst choices: AvatarChoices = {\n\t\tbrowI,\n\t\tdxI,\n\t\tdyI,\n\t\teyeI,\n\t\tfaceTiltI,\n\t\tflip,\n\t\tgapI,\n\t\tlookI,\n\t\tmouthI,\n\t\tshapeI,\n\t\ttiltI,\n\t};\n\tconst id = `fp${hash.toString(36)}-${shapeI}-${eyeI}-${mouthI}-${gapI}${frame.charAt(0)}`;\n\tconst avatar = renderAvatar({ choices, colours, frame, id, key });\n\treturn { ...avatar, hash, key };\n};\n\nexport const DEFAULT_CUSTOM_AVATAR_CHOICES = {\n\tbodyTilt: 0,\n\tbrows: \"none\",\n\teyeSpacing: \"medium\",\n\teyes: \"big\",\n\tfaceAngle: 0,\n\tfacePosition: { x: 0, y: 0 },\n\tlook: \"centre\",\n\tmirrored: false,\n\tmouth: \"smile\",\n\tshape: \"round\",\n} as const satisfies CustomAvatarChoices;\n\nconst getChoiceIndex = <Choice extends string>(\n\tchoices: readonly Choice[],\n\tvalue: Choice,\n\tlabel: string\n): number => {\n\tconst index = choices.indexOf(value);\n\tif (index === -1) {\n\t\tthrow new RangeError(`Unknown avatar ${label}: ${String(value)}`);\n\t}\n\treturn index;\n};\n\nconst requireIntegerInRange = (\n\tvalue: unknown,\n\tminimum: number,\n\tmaximum: number,\n\tlabel: string\n): number => {\n\tif (\n\t\ttypeof value !== \"number\" ||\n\t\t!Number.isInteger(value) ||\n\t\tvalue < minimum ||\n\t\tvalue > maximum\n\t) {\n\t\tthrow new RangeError(\n\t\t\t`${label} must be an integer from ${minimum} to ${maximum}.`\n\t\t);\n\t}\n\treturn value;\n};\n\nconst requireBoolean = (value: unknown, label: string): boolean => {\n\tif (typeof value !== \"boolean\") {\n\t\tthrow new TypeError(`${label} must be a boolean.`);\n\t}\n\treturn value;\n};\n\nexport const toAvatarChoices = (\n\tchoices: CustomAvatarChoices\n): AvatarChoices => {\n\tconst bodyTilt = requireIntegerInRange(\n\t\tchoices.bodyTilt,\n\t\t-14,\n\t\t14,\n\t\t\"Body tilt\"\n\t);\n\tif (bodyTilt % 2 !== 0) {\n\t\tthrow new RangeError(\"Body tilt must use two-degree increments.\");\n\t}\n\tconst faceAngle = requireIntegerInRange(\n\t\tchoices.faceAngle,\n\t\t-12,\n\t\t12,\n\t\t\"Face angle\"\n\t);\n\tconst positionX = requireIntegerInRange(\n\t\tchoices.facePosition.x,\n\t\t-2,\n\t\t2,\n\t\t\"Horizontal face position\"\n\t);\n\tconst positionY = requireIntegerInRange(\n\t\tchoices.facePosition.y,\n\t\t-1,\n\t\t1,\n\t\t\"Vertical face position\"\n\t);\n\n\treturn {\n\t\tbrowI: getChoiceIndex(AVATAR_CHOICES.brows, choices.brows, \"brows\"),\n\t\tdxI: positionX + 2,\n\t\tdyI: positionY + 1,\n\t\teyeI: getChoiceIndex(AVATAR_CHOICES.eyes, choices.eyes, \"eyes\"),\n\t\tfaceTiltI: faceAngle + 12,\n\t\tflip: requireBoolean(choices.mirrored, \"Mirrored\") ? 1 : 0,\n\t\tgapI: getChoiceIndex(AVATAR_CHOICES.gap, choices.eyeSpacing, \"eye spacing\"),\n\t\tlookI: getChoiceIndex(AVATAR_CHOICES.look, choices.look, \"look\"),\n\t\tmouthI: getChoiceIndex(AVATAR_CHOICES.mouth, choices.mouth, \"mouth\"),\n\t\tshapeI: getChoiceIndex(AVATAR_CHOICES.shape, choices.shape, \"shape\"),\n\t\ttiltI: bodyTilt / 2 + 7,\n\t};\n};\n\nexport const toCustomAvatarChoices = (\n\tchoices: AvatarChoices\n): CustomAvatarChoices => ({\n\tbodyTilt: (choices.tiltI - 7) * 2,\n\tbrows: getItem(AVATAR_CHOICES.brows, choices.browI),\n\teyeSpacing: getItem(AVATAR_CHOICES.gap, choices.gapI),\n\teyes: getItem(AVATAR_CHOICES.eyes, choices.eyeI),\n\tfaceAngle: choices.faceTiltI - 12,\n\tfacePosition: {\n\t\tx: choices.dxI - 2,\n\t\ty: choices.dyI - 1,\n\t},\n\tlook: getItem(AVATAR_CHOICES.look, choices.lookI),\n\tmirrored: choices.flip === 1,\n\tmouth: getItem(AVATAR_CHOICES.mouth, choices.mouthI),\n\tshape: getItem(AVATAR_CHOICES.shape, choices.shapeI),\n});\n\nexport const createCustomAvatar = ({\n\tchoices: customChoices,\n\tpalette = DEFAULT_AVATAR_PALETTE,\n\tframe = \"rounded\",\n}: CreateCustomAvatarOptions): CustomAvatarResult => {\n\tconst choices = toAvatarChoices(customChoices);\n\tconst signature = [\n\t\tchoices.shapeI,\n\t\tchoices.eyeI,\n\t\tchoices.lookI,\n\t\tchoices.mouthI,\n\t\tchoices.tiltI,\n\t\tchoices.dxI,\n\t\tchoices.dyI,\n\t\tchoices.gapI,\n\t\tchoices.flip,\n\t\tchoices.browI,\n\t\tchoices.faceTiltI,\n\t\tframe,\n\t\tpalette[0],\n\t\tpalette[1],\n\t].join(\"|\");\n\tconst technicalId = `fpc${hashAvatarSeed(signature).toString(36)}`;\n\tconst avatar = renderAvatar({\n\t\tchoices,\n\t\tcolours: palette,\n\t\tframe,\n\t\tid: technicalId,\n\t\tkey: \"custom avatar\",\n\t});\n\treturn { ...avatar, key: \"custom avatar\" };\n};\n\nexport const createAvatar = ({\n\tname,\n\tpalette = DEFAULT_AVATAR_PALETTE,\n\tframe = \"rounded\",\n\tchoices = null,\n}: CreateAvatarOptions): AvatarResult =>\n\tmakeAvatar(name, palette, frame, choices);\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blobz/avatar-core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Deterministic, framework-free SVG blob avatars.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"avatar",
|
|
7
|
+
"blob",
|
|
8
|
+
"deterministic",
|
|
9
|
+
"svg"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://blobz.lirena.in",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/lirena00/blobz/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/lirena00/blobz.git",
|
|
18
|
+
"directory": "packages/avatar-core"
|
|
19
|
+
},
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"type": "module",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/avatar.d.ts",
|
|
25
|
+
"import": "./dist/avatar.js",
|
|
26
|
+
"default": "./dist/avatar.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsup src/avatar.ts --format esm --dts --clean --sourcemap",
|
|
35
|
+
"check-types": "tsc --noEmit",
|
|
36
|
+
"prepublishOnly": "bun run check-types && bun run test && bun run build",
|
|
37
|
+
"test": "bun test"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@blobz/config": "0.0.0",
|
|
41
|
+
"@types/node": "^26.4.1",
|
|
42
|
+
"typescript": "^6.0.3"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public",
|
|
46
|
+
"registry": "https://registry.npmjs.org/"
|
|
47
|
+
}
|
|
48
|
+
}
|