quirkatar 1.0.0 → 1.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.
Files changed (3) hide show
  1. package/README.md +38 -13
  2. package/avatar.js +186 -67
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -31,11 +31,12 @@ Quirkatar generates hilarious, memorable, and utterly unique avatars from any st
31
31
  |---------|-------------|
32
32
  | ðŸŠķ **Zero Dependencies** | Pure vanilla JS + SVG. No bloat, no network requests, no drama |
33
33
  | ðŸŽē **Deterministic Seeds** | `"user@email.com"` always produces the same chaos |
34
+ | 😊 **6 Mood Controls** | Happy, sad, angry, surprised, chill, or random |
35
+ | ðŸŽĻ **4 Color Palettes** | Default, pastel, neon, monochrome |
34
36
  | 👀 **14 Eye Types** | Normal, cyclops, glasses, stars, hearts, wink, and more |
35
37
  | 😚 **11 Mouth Styles** | Smiles, fangs, tongues, teeth, zigzags... |
36
38
  | 👂 **7 Ear Options** | Cat, bear, bunny, alien, elf, robot, or none |
37
39
  | ðŸŽĐ **8 Headwear** | Hats, crowns, horns, headphones, bows |
38
- | ðŸŽĻ **37 Colors** | Hand-picked to look *chef's kiss* |
39
40
  | ðŸ’ū **Download as PNG** | One-click export at 512x512 |
40
41
  | ⚡ **Lightweight** | ~10KB, loads instantly |
41
42
 
@@ -46,7 +47,15 @@ Quirkatar generates hilarious, memorable, and utterly unique avatars from any st
46
47
  <script src="https://unpkg.com/quirkatar"></script>
47
48
  <div id="avatar"></div>
48
49
  <script>
50
+ // Random avatar
49
51
  document.getElementById('avatar').innerHTML = generateAvatarSvg('your-seed-here');
52
+
53
+ // Happy avatar with neon colors
54
+ document.getElementById('avatar').innerHTML = generateAvatarSvg('your-seed-here', {
55
+ mood: 'happy',
56
+ palette: 'neon',
57
+ size: 200
58
+ });
50
59
  </script>
51
60
  ```
52
61
 
@@ -58,8 +67,12 @@ npm install quirkatar
58
67
  ```javascript
59
68
  import { generateAvatarSvg } from 'quirkatar';
60
69
 
61
- // Generate a 200px avatar
62
- const avatar = generateAvatarSvg('hello-world', 200);
70
+ // Generate a 200px happy avatar
71
+ const avatar = generateAvatarSvg('hello-world', {
72
+ size: 200,
73
+ mood: 'happy',
74
+ palette: 'pastel'
75
+ });
63
76
  document.getElementById('profile').innerHTML = avatar;
64
77
  ```
65
78
 
@@ -71,28 +84,40 @@ Just download `avatar.js` and include it:
71
84
 
72
85
  ## API
73
86
 
74
- ### `generateAvatarSvg(seed, size, square, animated)`
87
+ ### `generateAvatarSvg(seed, options)`
75
88
 
76
89
  | Parameter | Type | Default | Description |
77
90
  |-----------|------|---------|-------------|
78
91
  | `seed` | `string` | *required* | Any string to generate the avatar from |
79
- | `size` | `number` | `100` | Width/height in pixels |
80
- | `square` | `boolean` | `false` | `true` for square, `false` for circle |
81
- | `animated` | `boolean` | `true` | Enable breathing, blinking, twitching animations |
92
+ | `options.size` | `number` | `100` | Width/height in pixels |
93
+ | `options.square` | `boolean` | `false` | `true` for square, `false` for circle |
94
+ | `options.animated` | `boolean` | `true` | Enable breathing, blinking, twitching animations |
95
+ | `options.mood` | `string` | `'random'` | `'random'`, `'happy'`, `'sad'`, `'angry'`, `'surprised'`, `'chill'` |
96
+ | `options.palette` | `string` | `'default'` | `'default'`, `'pastel'`, `'neon'`, `'monochrome'` |
97
+ | `options.eyebrows` | `boolean` | `true` | Show expressive eyebrows |
82
98
 
83
99
  **Returns:** SVG string ready to inject into DOM
84
100
 
85
101
  ## Examples
86
102
 
87
103
  ```javascript
88
- // Default circular avatar
104
+ // Default avatar
89
105
  generateAvatarSvg('user123')
90
106
 
107
+ // Happy avatar with neon colors
108
+ generateAvatarSvg('user123', { mood: 'happy', palette: 'neon' })
109
+
110
+ // Sad avatar with pastel colors
111
+ generateAvatarSvg('user123', { mood: 'sad', palette: 'pastel', size: 200 })
112
+
91
113
  // Large square avatar, no animation
92
- generateAvatarSvg('team-member', 256, true, false)
114
+ generateAvatarSvg('team-member', { size: 256, square: true, animated: false })
93
115
 
94
116
  // Use email as seed (consistent across sessions)
95
- generateAvatarSvg('john@company.com', 80)
117
+ generateAvatarSvg('john@company.com', { size: 80, mood: 'chill' })
118
+
119
+ // Monochrome avatar for professional UI
120
+ generateAvatarSvg('user-id', { palette: 'monochrome', mood: 'chill' })
96
121
  ```
97
122
 
98
123
  ## Use Cases
@@ -108,11 +133,11 @@ generateAvatarSvg('john@company.com', 80)
108
133
 
109
134
  With the current feature set, Quirkatar can generate:
110
135
 
111
- **8 head shapes × 7 ears × 14 eyes × 11 mouths × 7 accessories × 8 headwear × 3 cheeks × 37 colors =**
136
+ **8 head shapes × 7 ears × 14 eyes × 11 mouths × 7 accessories × 8 headwear × 3 cheeks × 4 eyebrows × 6 moods × 4 palettes =**
112
137
 
113
- > **~7.5 million unique avatars**
138
+ > **~34 million unique avatars**
114
139
 
115
- ...and that's not even counting color combinations.
140
+ ...and that's not even counting color variations.
116
141
 
117
142
  ## Browser Support
118
143
 
package/avatar.js CHANGED
@@ -1,3 +1,6 @@
1
+ // Quirkatar - The quirkiest avatar generator
2
+ // https://github.com/Nitty-Gritty-Design/quirkatar
3
+
1
4
  // Simple PRNG
2
5
  function xmur3(str) {
3
6
  let h = 1779033703 ^ str.length;
@@ -21,32 +24,127 @@ function mulberry32(a) {
21
24
  };
22
25
  }
23
26
 
24
- const COLORS = [
25
- '#FFAD08', '#EDD75A', '#73B06F', '#0C8F8F', '#405059',
26
- '#FF9F1C', '#FFBF69', '#CBF3F0', '#2EC4B6',
27
- '#264653', '#2A9D8F', '#E9C46A', '#F4A261', '#E76F51',
28
- '#FFCDB2', '#FFB4A2', '#E5989B', '#B5838D', '#6D6875',
29
- '#8ECAE6', '#219EBC', '#023047', '#FFB703', '#FB8500',
30
- '#CDB4DB', '#FFC8DD', '#FFAFCC', '#BDE0FE', '#A2D2FF',
31
- '#F94144', '#F3722C', '#F8961E', '#F9C74F', '#90BE6D', '#43AA8B', '#577590'
32
- ];
27
+ // Color palettes
28
+ const PALETTES = {
29
+ default: [
30
+ '#FFAD08', '#EDD75A', '#73B06F', '#0C8F8F', '#405059',
31
+ '#FF9F1C', '#FFBF69', '#CBF3F0', '#2EC4B6', '#264653',
32
+ '#2A9D8F', '#E9C46A', '#F4A261', '#E76F51', '#FFCDB2',
33
+ '#FFB4A2', '#E5989B', '#B5838D', '#6D6875', '#8ECAE6',
34
+ '#219EBC', '#023047', '#FFB703', '#FB8500', '#CDB4DB',
35
+ '#FFC8DD', '#FFAFCC', '#BDE0FE', '#A2D2FF', '#F94144',
36
+ '#F3722C', '#F8961E', '#F9C74F', '#90BE6D', '#43AA8B', '#577590'
37
+ ],
38
+ pastel: [
39
+ '#FFB5E8', '#FF9CEE', '#FFCCF9', '#FCC2FF', '#F6A6FF',
40
+ '#B28DFF', '#C5A3FF', '#D5AAFF', '#ECD4FF', '#AFCBFF',
41
+ '#AFF8DB', '#BFFCC6', '#E7FFAC', '#FFFFD1', '#FFC9DE',
42
+ '#FFABAB', '#FFDAC1', '#B5EAD7', '#C7CEEA', '#E2F0CB'
43
+ ],
44
+ neon: [
45
+ '#FF00FF', '#00FFFF', '#FF00AA', '#AAFF00', '#00FF00',
46
+ '#FF6600', '#0066FF', '#FFFF00', '#FF0066', '#6600FF',
47
+ '#00FF66', '#FF3300', '#0033FF', '#FF0099', '#99FF00'
48
+ ],
49
+ monochrome: [
50
+ '#1a1a1a', '#2d2d2d', '#404040', '#535353', '#666666',
51
+ '#7a7a7a', '#8d8d8d', '#a0a0a0', '#b3b3b3', '#c6c6c6',
52
+ '#d9d9d9', '#ececec', '#ffffff'
53
+ ]
54
+ };
55
+
56
+ // Mood configurations
57
+ const MOODS = {
58
+ happy: {
59
+ eyes: ['happy', 'normal', 'wink', 'star', 'heart'],
60
+ mouth: ['smile', 'openSmile', 'tongue', 'cat'],
61
+ eyebrows: 'raised'
62
+ },
63
+ sad: {
64
+ eyes: ['sleepy', 'normal', 'wink'],
65
+ mouth: ['frown', 'o', 'zigzag'],
66
+ eyebrows: 'droopy'
67
+ },
68
+ angry: {
69
+ eyes: ['normal', 'cyclops', 'wink'],
70
+ mouth: ['frown', 'teeth', 'zigzag'],
71
+ eyebrows: 'angry'
72
+ },
73
+ surprised: {
74
+ eyes: ['normal', 'cyclops', 'three', 'star'],
75
+ mouth: ['o', 'openSmile'],
76
+ eyebrows: 'raised'
77
+ },
78
+ chill: {
79
+ eyes: ['sleepy', 'normal', 'wink', 'glasses'],
80
+ mouth: ['smile', 'cat', 'o'],
81
+ eyebrows: 'neutral'
82
+ },
83
+ random: null
84
+ };
85
+
86
+ /**
87
+ * Generate a quirky avatar SVG
88
+ * @param {string} seed - The seed string for deterministic generation
89
+ * @param {Object|number} options - Options object or size (legacy)
90
+ * @param {number} options.size - Avatar size in pixels (default: 100)
91
+ * @param {boolean} options.square - Square avatar instead of circle (default: false)
92
+ * @param {boolean} options.animated - Enable animations (default: true)
93
+ * @param {string} options.mood - 'random'|'happy'|'sad'|'angry'|'surprised'|'chill' (default: 'random')
94
+ * @param {string} options.palette - 'default'|'pastel'|'neon'|'monochrome' (default: 'default')
95
+ * @param {boolean} options.eyebrows - Show eyebrows (default: true)
96
+ * @returns {string} SVG string
97
+ */
98
+ function generateAvatarSvg(seed, options = {}) {
99
+ // Legacy support: generateAvatarSvg(seed, size, square, animated)
100
+ if (typeof options === 'number' || typeof options === 'boolean') {
101
+ options = {
102
+ size: typeof options === 'number' ? options : 100,
103
+ square: typeof options === 'boolean' ? options : false,
104
+ animated: arguments[3] !== undefined ? arguments[3] : true
105
+ };
106
+ }
107
+
108
+ const {
109
+ size = 100,
110
+ square = false,
111
+ animated = true,
112
+ mood = 'random',
113
+ palette = 'default',
114
+ eyebrows: showEyebrows = true
115
+ } = options;
33
116
 
34
- function generateAvatarSvg(seed, size = 100, square = false, animated = true) {
35
117
  const seedNum = xmur3(seed)();
36
118
  const rand = mulberry32(seedNum);
37
119
 
38
120
  const pick = (arr) => arr[Math.floor(rand() * arr.length)];
39
121
 
122
+ // Get colors from palette
123
+ const COLORS = PALETTES[palette] || PALETTES.default;
40
124
  const bgColor = pick(COLORS);
41
125
  let headColor = pick(COLORS);
42
126
  while (headColor === bgColor) headColor = pick(COLORS);
43
127
  let detailColor = pick(COLORS);
44
128
  while (detailColor === bgColor || detailColor === headColor) detailColor = pick(COLORS);
45
129
 
130
+ // Get mood config
131
+ const moodConfig = MOODS[mood] || MOODS.random;
132
+
133
+ // Select features based on mood
46
134
  const headShape = pick(['circle', 'squircle', 'blob', 'triangle', 'hexagon', 'diamond', 'star', 'cloud']);
47
135
  const ears = pick(['none', 'cat', 'bear', 'bunny', 'alien', 'elf', 'robot']);
48
- const eyes = pick(['normal', 'happy', 'sleepy', 'cyclops', 'glasses', 'three', 'wink', 'star', 'heart']);
49
- const mouth = pick(['smile', 'openSmile', 'frown', 'cat', 'vampire', 'o', 'zigzag', 'tongue', 'teeth']);
136
+
137
+ // Eyes based on mood
138
+ const allEyes = ['normal', 'happy', 'sleepy', 'cyclops', 'glasses', 'three', 'wink', 'star', 'heart'];
139
+ const eyes = moodConfig ? pick(moodConfig.eyes) : pick(allEyes);
140
+
141
+ // Mouth based on mood
142
+ const allMouths = ['smile', 'openSmile', 'frown', 'cat', 'vampire', 'o', 'zigzag', 'tongue', 'teeth'];
143
+ const mouth = moodConfig ? pick(moodConfig.mouth) : pick(allMouths);
144
+
145
+ // Eyebrows based on mood
146
+ const eyebrowType = moodConfig ? moodConfig.eyebrows : pick(['neutral', 'raised', 'droopy', 'angry']);
147
+
50
148
  const accessory = pick(['none', 'none', 'none', 'freckles', 'blush', 'mustache', 'beard']);
51
149
  const headwear = pick(['none', 'none', 'none', 'hat', 'crown', 'horns', 'headphones', 'bow']);
52
150
  const cheeks = pick(['none', 'rosy', 'dimples']);
@@ -143,96 +241,115 @@ function generateAvatarSvg(seed, size = 100, square = false, animated = true) {
143
241
  svg += `<path d="M 25 60 C 10 60 10 45 25 45 C 25 30 45 25 55 35 C 65 20 90 30 85 50 C 95 55 90 70 75 70 L 25 70 C 15 70 15 60 25 60 Z" fill="${headColor}" />`;
144
242
  }
145
243
 
244
+ // Eyebrows (above eyes)
245
+ if (showEyebrows) {
246
+ svg += `<g fill="none" stroke="#111" stroke-width="3" stroke-linecap="round">`;
247
+ if (eyebrowType === 'raised') {
248
+ svg += `<path d="M 25 35 Q 35 30 45 35" />
249
+ <path d="M 55 35 Q 65 30 75 35" />`;
250
+ } else if (eyebrowType === 'droopy') {
251
+ svg += `<path d="M 25 35 Q 35 40 45 35" />
252
+ <path d="M 55 35 Q 65 40 75 35" />`;
253
+ } else if (eyebrowType === 'angry') {
254
+ svg += `<path d="M 25 40 L 45 35" />
255
+ <path d="M 55 35 L 75 40" />`;
256
+ } else {
257
+ svg += `<path d="M 25 35 L 45 35" />
258
+ <path d="M 55 35 L 75 35" />`;
259
+ }
260
+ svg += `</g>`;
261
+ }
262
+
146
263
  // Eyes
147
264
  svg += `<g class="quirks-blink">`;
148
265
  if (eyes === 'normal') {
149
- svg += `<circle cx="35" cy="45" r="6" fill="#111" />
150
- <circle cx="65" cy="45" r="6" fill="#111" />`;
266
+ svg += `<circle cx="35" cy="48" r="6" fill="#111" />
267
+ <circle cx="65" cy="48" r="6" fill="#111" />`;
151
268
  } else if (eyes === 'happy') {
152
- svg += `<path d="M 25 45 Q 35 35 45 45" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />
153
- <path d="M 55 45 Q 65 35 75 45" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />`;
269
+ svg += `<path d="M 25 48 Q 35 38 45 48" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />
270
+ <path d="M 55 48 Q 65 38 75 48" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />`;
154
271
  } else if (eyes === 'sleepy') {
155
- svg += `<path d="M 25 45 Q 35 55 45 45" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />
156
- <path d="M 55 45 Q 65 55 75 45" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />`;
272
+ svg += `<path d="M 25 48 Q 35 58 45 48" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />
273
+ <path d="M 55 48 Q 65 58 75 48" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />`;
157
274
  } else if (eyes === 'cyclops') {
158
- svg += `<circle cx="50" cy="40" r="14" fill="#fff" />
159
- <circle cx="50" cy="40" r="6" fill="#111" />`;
275
+ svg += `<circle cx="50" cy="45" r="14" fill="#fff" />
276
+ <circle cx="50" cy="45" r="6" fill="#111" />`;
160
277
  } else if (eyes === 'glasses') {
161
- svg += `<rect x="20" y="35" width="26" height="18" rx="4" fill="none" stroke="#111" stroke-width="4" />
162
- <rect x="54" y="35" width="26" height="18" rx="4" fill="none" stroke="#111" stroke-width="4" />
163
- <line x1="46" y1="44" x2="54" y2="44" stroke="#111" stroke-width="4" />
164
- <circle cx="33" cy="44" r="4" fill="#111" />
165
- <circle cx="67" cy="44" r="4" fill="#111" />`;
278
+ svg += `<rect x="20" y="38" width="26" height="18" rx="4" fill="none" stroke="#111" stroke-width="4" />
279
+ <rect x="54" y="38" width="26" height="18" rx="4" fill="none" stroke="#111" stroke-width="4" />
280
+ <line x1="46" y1="47" x2="54" y2="47" stroke="#111" stroke-width="4" />
281
+ <circle cx="33" cy="47" r="4" fill="#111" />
282
+ <circle cx="67" cy="47" r="4" fill="#111" />`;
166
283
  } else if (eyes === 'three') {
167
- svg += `<circle cx="50" cy="35" r="5" fill="#111" />
168
- <circle cx="30" cy="50" r="5" fill="#111" />
169
- <circle cx="70" cy="50" r="5" fill="#111" />`;
284
+ svg += `<circle cx="50" cy="38" r="5" fill="#111" />
285
+ <circle cx="30" cy="53" r="5" fill="#111" />
286
+ <circle cx="70" cy="53" r="5" fill="#111" />`;
170
287
  } else if (eyes === 'wink') {
171
- svg += `<circle cx="35" cy="45" r="6" fill="#111" />
172
- <path d="M 55 45 Q 65 35 75 45" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />`;
288
+ svg += `<circle cx="35" cy="48" r="6" fill="#111" />
289
+ <path d="M 55 48 Q 65 38 75 48" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />`;
173
290
  } else if (eyes === 'star') {
174
- svg += `<polygon points="35,39 37,43 41,43 38,46 39,50 35,48 31,50 32,46 29,43 33,43" fill="#111" />
175
- <polygon points="65,39 67,43 71,43 68,46 69,50 65,48 61,50 62,46 59,43 63,43" fill="#111" />`;
291
+ svg += `<polygon points="35,42 37,46 41,46 38,49 39,53 35,51 31,53 32,49 29,46 33,46" fill="#111" />
292
+ <polygon points="65,42 67,46 71,46 68,49 69,53 65,51 61,53 62,49 59,46 63,46" fill="#111" />`;
176
293
  } else if (eyes === 'heart') {
177
- svg += `<path d="M 35 48 C 35 44 30 44 30 48 C 30 52 35 55 35 55 C 35 55 40 52 40 48 C 40 44 35 44 35 48 Z" fill="#e91e63" />
178
- <path d="M 65 48 C 65 44 60 44 60 48 C 60 52 65 55 65 55 C 65 55 70 52 70 48 C 70 44 65 44 65 48 Z" fill="#e91e63" />`;
294
+ svg += `<path d="M 35 51 C 35 47 30 47 30 51 C 30 55 35 58 35 58 C 35 58 40 55 40 51 C 40 47 35 47 35 51 Z" fill="#e91e63" />
295
+ <path d="M 65 51 C 65 47 60 47 60 51 C 60 55 65 58 65 58 C 65 58 70 55 70 51 C 70 47 65 47 65 51 Z" fill="#e91e63" />`;
179
296
  }
180
297
  svg += `</g>`;
181
298
 
182
299
  // Mouth
183
300
  if (mouth === 'smile') {
184
- svg += `<g class="quirks-smile"><path d="M 35 65 Q 50 80 65 65" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" /></g>`;
301
+ svg += `<g class="quirks-smile"><path d="M 35 68 Q 50 83 65 68" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" /></g>`;
185
302
  } else if (mouth === 'openSmile') {
186
- svg += `<g class="quirks-talk"><path d="M 30 60 Q 50 85 70 60 Z" fill="#111" />
187
- <path d="M 40 70 Q 50 75 60 70 Q 50 65 40 70" fill="#ff6b6b" /></g>`;
303
+ svg += `<g class="quirks-talk"><path d="M 30 63 Q 50 88 70 63 Z" fill="#111" />
304
+ <path d="M 40 73 Q 50 78 60 73 Q 50 68 40 73" fill="#ff6b6b" /></g>`;
188
305
  } else if (mouth === 'frown') {
189
- svg += `<g class="quirks-frown"><path d="M 35 75 Q 50 60 65 75" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" /></g>`;
306
+ svg += `<g class="quirks-frown"><path d="M 35 78 Q 50 63 65 78" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" /></g>`;
190
307
  } else if (mouth === 'cat') {
191
- svg += `<g class="quirks-twitch"><path d="M 35 65 Q 42.5 75 50 65 Q 57.5 75 65 65" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" /></g>`;
308
+ svg += `<g class="quirks-twitch"><path d="M 35 68 Q 42.5 78 50 68 Q 57.5 78 65 68" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" /></g>`;
192
309
  } else if (mouth === 'vampire') {
193
- svg += `<g class="quirks-smile"><path d="M 35 65 Q 50 80 65 65" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />
194
- <polygon points="40,68 45,69 42.5,75" fill="#fff" />
195
- <polygon points="60,68 55,69 57.5,75" fill="#fff" /></g>`;
310
+ svg += `<g class="quirks-smile"><path d="M 35 68 Q 50 83 65 68" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />
311
+ <polygon points="40,71 45,72 42.5,78" fill="#fff" />
312
+ <polygon points="60,71 55,72 57.5,78" fill="#fff" /></g>`;
196
313
  } else if (mouth === 'o') {
197
- svg += `<g class="quirks-talk"><circle cx="50" cy="70" r="8" fill="#111" /></g>`;
314
+ svg += `<g class="quirks-talk"><circle cx="50" cy="73" r="8" fill="#111" /></g>`;
198
315
  } else if (mouth === 'zigzag') {
199
- svg += `<g class="quirks-frown"><polyline points="35,65 42.5,70 50,65 57.5,70 65,65" stroke="#111" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none" /></g>`;
316
+ svg += `<g class="quirks-frown"><polyline points="35,68 42.5,73 50,68 57.5,73 65,68" stroke="#111" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none" /></g>`;
200
317
  } else if (mouth === 'tongue') {
201
- svg += `<g class="quirks-smile"><path d="M 35 65 Q 50 80 65 65" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />
202
- <ellipse cx="50" cy="75" rx="8" ry="10" fill="#e91e63" /></g>`;
318
+ svg += `<g class="quirks-smile"><path d="M 35 68 Q 50 83 65 68" stroke="#111" stroke-width="4" stroke-linecap="round" fill="none" />
319
+ <ellipse cx="50" cy="78" rx="8" ry="10" fill="#e91e63" /></g>`;
203
320
  } else if (mouth === 'teeth') {
204
- svg += `<g class="quirks-talk"><rect x="32" y="62" width="36" height="18" rx="4" fill="#111" />
205
- <rect x="35" y="62" width="30" height="8" fill="#fff" />
206
- <line x1="40" y1="62" x2="40" y2="70" stroke="#111" stroke-width="1" />
207
- <line x1="50" y1="62" x2="50" y2="70" stroke="#111" stroke-width="1" />
208
- <line x1="60" y1="62" x2="60" y2="70" stroke="#111" stroke-width="1" /></g>`;
321
+ svg += `<g class="quirks-talk"><rect x="32" y="65" width="36" height="18" rx="4" fill="#111" />
322
+ <rect x="35" y="65" width="30" height="8" fill="#fff" />
323
+ <line x1="40" y1="65" x2="40" y2="73" stroke="#111" stroke-width="1" />
324
+ <line x1="50" y1="65" x2="50" y2="73" stroke="#111" stroke-width="1" />
325
+ <line x1="60" y1="65" x2="60" y2="73" stroke="#111" stroke-width="1" /></g>`;
209
326
  }
210
327
 
211
328
  // Accessory (face details)
212
329
  if (accessory === 'freckles') {
213
- svg += `<circle cx="25" cy="55" r="2" fill="#111" opacity="0.3" />
214
- <circle cx="30" cy="58" r="2" fill="#111" opacity="0.3" />
215
- <circle cx="20" cy="58" r="2" fill="#111" opacity="0.3" />
216
- <circle cx="75" cy="55" r="2" fill="#111" opacity="0.3" />
217
- <circle cx="70" cy="58" r="2" fill="#111" opacity="0.3" />
218
- <circle cx="80" cy="58" r="2" fill="#111" opacity="0.3" />`;
330
+ svg += `<circle cx="25" cy="58" r="2" fill="#111" opacity="0.3" />
331
+ <circle cx="30" cy="61" r="2" fill="#111" opacity="0.3" />
332
+ <circle cx="20" cy="61" r="2" fill="#111" opacity="0.3" />
333
+ <circle cx="75" cy="58" r="2" fill="#111" opacity="0.3" />
334
+ <circle cx="70" cy="61" r="2" fill="#111" opacity="0.3" />
335
+ <circle cx="80" cy="61" r="2" fill="#111" opacity="0.3" />`;
219
336
  } else if (accessory === 'blush') {
220
- svg += `<ellipse cx="25" cy="55" rx="8" ry="5" fill="#ff6b6b" opacity="0.5" />
221
- <ellipse cx="75" cy="55" rx="8" ry="5" fill="#ff6b6b" opacity="0.5" />`;
337
+ svg += `<ellipse cx="25" cy="58" rx="8" ry="5" fill="#ff6b6b" opacity="0.5" />
338
+ <ellipse cx="75" cy="58" rx="8" ry="5" fill="#ff6b6b" opacity="0.5" />`;
222
339
  } else if (accessory === 'mustache') {
223
- svg += `<path d="M 30 60 Q 50 50 70 60 Q 60 65 50 62 Q 40 65 30 60 Z" fill="#111" />`;
340
+ svg += `<path d="M 30 63 Q 50 53 70 63 Q 60 68 50 65 Q 40 68 30 63 Z" fill="#111" />`;
224
341
  } else if (accessory === 'beard') {
225
- svg += `<path d="M 30 65 Q 35 85 50 88 Q 65 85 70 65 Q 60 70 50 72 Q 40 70 30 65 Z" fill="${headColor}" opacity="0.8" />
226
- <path d="M 35 68 Q 40 78 50 80 Q 60 78 65 68" stroke="#111" stroke-width="2" fill="none" opacity="0.3" />`;
342
+ svg += `<path d="M 30 68 Q 35 88 50 91 Q 65 88 70 68 Q 60 73 50 75 Q 40 73 30 68 Z" fill="${headColor}" opacity="0.8" />
343
+ <path d="M 35 71 Q 40 81 50 83 Q 60 81 65 71" stroke="#111" stroke-width="2" fill="none" opacity="0.3" />`;
227
344
  }
228
345
 
229
346
  // Cheeks
230
347
  if (cheeks === 'rosy') {
231
- svg += `<circle cx="25" cy="58" r="6" fill="#ffb6c1" opacity="0.4" />
232
- <circle cx="75" cy="58" r="6" fill="#ffb6c1" opacity="0.4" />`;
348
+ svg += `<circle cx="25" cy="61" r="6" fill="#ffb6c1" opacity="0.4" />
349
+ <circle cx="75" cy="61" r="6" fill="#ffb6c1" opacity="0.4" />`;
233
350
  } else if (cheeks === 'dimples') {
234
- svg += `<path d="M 22 58 Q 25 62 28 58" stroke="#111" stroke-width="2" fill="none" opacity="0.3" />
235
- <path d="M 72 58 Q 75 62 78 58" stroke="#111" stroke-width="2" fill="none" opacity="0.3" />`;
351
+ svg += `<path d="M 22 61 Q 25 65 28 61" stroke="#111" stroke-width="2" fill="none" opacity="0.3" />
352
+ <path d="M 72 61 Q 75 65 78 61" stroke="#111" stroke-width="2" fill="none" opacity="0.3" />`;
236
353
  }
237
354
 
238
355
  svg += `</g>`;
@@ -267,7 +384,9 @@ function generateAvatarSvg(seed, size = 100, square = false, animated = true) {
267
384
 
268
385
  // Export for both ES modules and CommonJS
269
386
  if (typeof module !== 'undefined' && module.exports) {
270
- module.exports = { generateAvatarSvg };
387
+ module.exports = { generateAvatarSvg, PALETTES, MOODS };
271
388
  } else if (typeof window !== 'undefined') {
272
389
  window.generateAvatarSvg = generateAvatarSvg;
390
+ window.PALETTES = PALETTES;
391
+ window.MOODS = MOODS;
273
392
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quirkatar",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A free, lightweight, zero-dependency procedural avatar generator. Create unique, quirky profile pictures from any seed string.",
5
5
  "main": "avatar.js",
6
6
  "type": "module",