quirkatar 1.0.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 (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -0
  3. package/avatar.js +273 -0
  4. package/package.json +47 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nitty-Gritty-Design
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ <div align="center">
2
+ <img src="assets/Quirkatar.jpg" width="972px" alt="Quirkatar - Quirky Avatar Generator" />
3
+ <h1>👾 Quirkatar</h1>
4
+ <p><strong>The weirdest, quirkiest avatar generator on the internet.</strong></p>
5
+ <p>Zero dependencies. Infinite chaos.</p>
6
+
7
+ <a href="https://nitty-gritty-design.github.io/quirkatar-avatar-generator/">
8
+ <img src="https://img.shields.io/badge/LIVE_DEMO-Try_It_Now-brightgreen?style=for-the-badge&logo=google-chrome" />
9
+ </a>
10
+ <a href="https://www.npmjs.com/package/quirkatar">
11
+ <img src="https://img.shields.io/npm/v/quirkatar?style=for-the-badge&color=cb3837" />
12
+ </a>
13
+ <a href="https://github.com/Nitty-Gritty-Design/quirkatar-avatar-generator/blob/main/LICENSE">
14
+ <img src="https://img.shields.io/github/license/Nitty-Gritty-Design/quirkatar-avatar-generator?style=for-the-badge" />
15
+ </a>
16
+ </div>
17
+
18
+ ---
19
+
20
+ ## Why Quirkatar?
21
+
22
+ Tired of boring default avatars? Your users deserve **personality**.
23
+
24
+ Quirkatar generates hilarious, memorable, and utterly unique avatars from any string. Same seed = same weirdo. Perfect for user profiles, team collabs, or just trolling your friends with cursed profile pics.
25
+
26
+ ![Avatar Grid](assets/Quirkatar_Grid.jpg)
27
+
28
+ ## Features
29
+
30
+ | Feature | Description |
31
+ |---------|-------------|
32
+ | đŸĒļ **Zero Dependencies** | Pure vanilla JS + SVG. No bloat, no network requests, no drama |
33
+ | 🎲 **Deterministic Seeds** | `"user@email.com"` always produces the same chaos |
34
+ | 👀 **14 Eye Types** | Normal, cyclops, glasses, stars, hearts, wink, and more |
35
+ | đŸ˜ē **11 Mouth Styles** | Smiles, fangs, tongues, teeth, zigzags... |
36
+ | 👂 **7 Ear Options** | Cat, bear, bunny, alien, elf, robot, or none |
37
+ | 🎩 **8 Headwear** | Hats, crowns, horns, headphones, bows |
38
+ | 🎨 **37 Colors** | Hand-picked to look *chef's kiss* |
39
+ | 💾 **Download as PNG** | One-click export at 512x512 |
40
+ | ⚡ **Lightweight** | ~10KB, loads instantly |
41
+
42
+ ## Quick Start
43
+
44
+ ### Browser (CDN)
45
+ ```html
46
+ <script src="https://unpkg.com/quirkatar"></script>
47
+ <div id="avatar"></div>
48
+ <script>
49
+ document.getElementById('avatar').innerHTML = generateAvatarSvg('your-seed-here');
50
+ </script>
51
+ ```
52
+
53
+ ### npm
54
+ ```bash
55
+ npm install quirkatar
56
+ ```
57
+
58
+ ```javascript
59
+ import { generateAvatarSvg } from 'quirkatar';
60
+
61
+ // Generate a 200px avatar
62
+ const avatar = generateAvatarSvg('hello-world', 200);
63
+ document.getElementById('profile').innerHTML = avatar;
64
+ ```
65
+
66
+ ### Vanilla HTML
67
+ Just download `avatar.js` and include it:
68
+ ```html
69
+ <script src="avatar.js"></script>
70
+ ```
71
+
72
+ ## API
73
+
74
+ ### `generateAvatarSvg(seed, size, square, animated)`
75
+
76
+ | Parameter | Type | Default | Description |
77
+ |-----------|------|---------|-------------|
78
+ | `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 |
82
+
83
+ **Returns:** SVG string ready to inject into DOM
84
+
85
+ ## Examples
86
+
87
+ ```javascript
88
+ // Default circular avatar
89
+ generateAvatarSvg('user123')
90
+
91
+ // Large square avatar, no animation
92
+ generateAvatarSvg('team-member', 256, true, false)
93
+
94
+ // Use email as seed (consistent across sessions)
95
+ generateAvatarSvg('john@company.com', 80)
96
+ ```
97
+
98
+ ## Use Cases
99
+
100
+ - **User Profiles** - Default avatars that don't suck
101
+ - **Team Pages** - Fun avatars for your team directory
102
+ - **Comment Systems** - Anonymous but unique identities
103
+ - **Game Characters** - Quick character generation
104
+ - **Demo Data** - Placeholder avatars that look intentional
105
+ - **Social Experiments** - Watch people argue over whose avatar is weirder
106
+
107
+ ## Combos for Days
108
+
109
+ With the current feature set, Quirkatar can generate:
110
+
111
+ **8 head shapes × 7 ears × 14 eyes × 11 mouths × 7 accessories × 8 headwear × 3 cheeks × 37 colors =**
112
+
113
+ > **~7.5 million unique avatars**
114
+
115
+ ...and that's not even counting color combinations.
116
+
117
+ ## Browser Support
118
+
119
+ Works everywhere SVG works:
120
+ - Chrome/Edge (all versions)
121
+ - Firefox (all versions)
122
+ - Safari 6+
123
+ - Mobile browsers
124
+
125
+ ## Contributing
126
+
127
+ Found a bug? Want more cursed features?
128
+
129
+ 1. Fork it
130
+ 2. Create your feature branch (`git checkout -b feature/amazing`)
131
+ 3. Commit your changes (`git commit -m 'Add more chaos'`)
132
+ 4. Push to the branch (`git push origin feature/amazing`)
133
+ 5. Open a Pull Request
134
+
135
+ ## License
136
+
137
+ MIT - Do whatever you want. Make the internet weirder. 🎉
138
+
139
+ ---
140
+
141
+ <p align="center">
142
+ Made with ☕ and questionable design choices by <a href="https://github.com/Nitty-Gritty-Design">Nitty-Gritty-Design</a>
143
+ </p>
package/avatar.js ADDED
@@ -0,0 +1,273 @@
1
+ // Simple PRNG
2
+ function xmur3(str) {
3
+ let h = 1779033703 ^ str.length;
4
+ for (let i = 0; i < str.length; i++) {
5
+ h = Math.imul(h ^ str.charCodeAt(i), 3432918353);
6
+ h = (h << 13) | (h >>> 19);
7
+ }
8
+ return function () {
9
+ h = Math.imul(h ^ (h >>> 16), 2246822507);
10
+ h = Math.imul(h ^ (h >>> 13), 3266489909);
11
+ return (h ^= h >>> 16) >>> 0;
12
+ };
13
+ }
14
+
15
+ function mulberry32(a) {
16
+ return function () {
17
+ let t = (a += 0x6d2b79f5);
18
+ t = Math.imul(t ^ (t >>> 15), t | 1);
19
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
20
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
21
+ };
22
+ }
23
+
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
+ ];
33
+
34
+ function generateAvatarSvg(seed, size = 100, square = false, animated = true) {
35
+ const seedNum = xmur3(seed)();
36
+ const rand = mulberry32(seedNum);
37
+
38
+ const pick = (arr) => arr[Math.floor(rand() * arr.length)];
39
+
40
+ const bgColor = pick(COLORS);
41
+ let headColor = pick(COLORS);
42
+ while (headColor === bgColor) headColor = pick(COLORS);
43
+ let detailColor = pick(COLORS);
44
+ while (detailColor === bgColor || detailColor === headColor) detailColor = pick(COLORS);
45
+
46
+ const headShape = pick(['circle', 'squircle', 'blob', 'triangle', 'hexagon', 'diamond', 'star', 'cloud']);
47
+ 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']);
50
+ const accessory = pick(['none', 'none', 'none', 'freckles', 'blush', 'mustache', 'beard']);
51
+ const headwear = pick(['none', 'none', 'none', 'hat', 'crown', 'horns', 'headphones', 'bow']);
52
+ const cheeks = pick(['none', 'rosy', 'dimples']);
53
+
54
+ let svg = `<svg width="${size}" height="${size}" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">`;
55
+
56
+ if (animated) {
57
+ svg += `
58
+ <style>
59
+ @keyframes quirksBreathe {
60
+ 0%, 100% { transform: scale(1); }
61
+ 50% { transform: scale(1.05) translateY(-3px); }
62
+ }
63
+ @keyframes quirksBlink {
64
+ 0%, 94%, 100% { transform: scaleY(1); }
65
+ 96% { transform: scaleY(0.01); }
66
+ }
67
+ @keyframes quirksTwitch {
68
+ 0%, 90%, 100% { transform: rotate(0deg); }
69
+ 93% { transform: rotate(8deg); }
70
+ 96% { transform: rotate(-8deg); }
71
+ }
72
+ @keyframes quirksSmile {
73
+ 0%, 100% { transform: translateY(0); }
74
+ 50% { transform: translateY(-3px) scale(1.1); }
75
+ }
76
+ @keyframes quirksFrown {
77
+ 0%, 100% { transform: translateY(0); }
78
+ 50% { transform: translateY(6px); }
79
+ }
80
+ @keyframes quirksTalk {
81
+ 0%, 100% { transform: scaleY(1); }
82
+ 50% { transform: scaleY(1.3); }
83
+ }
84
+ .quirks-breathe { animation: quirksBreathe 4s infinite ease-in-out; transform-origin: 50px 50px; }
85
+ .quirks-blink { animation: quirksBlink 4s infinite; transform-origin: 50px 45px; }
86
+ .quirks-twitch { animation: quirksTwitch 5s infinite ease-in-out; transform-origin: 50px 50px; }
87
+ .quirks-smile { animation: quirksSmile 3s infinite ease-in-out; transform-origin: 50px 70px; }
88
+ .quirks-frown { animation: quirksFrown 4s infinite ease-in-out; transform-origin: 50px 70px; }
89
+ .quirks-talk { animation: quirksTalk 0.5s infinite alternate; transform-origin: 50px 70px; }
90
+ </style>
91
+ `;
92
+ }
93
+
94
+ // Background
95
+ svg += `<rect width="100" height="100" fill="${bgColor}" rx="${square ? 0 : 50}" />`;
96
+
97
+ // Ears
98
+ svg += `<g class="quirks-twitch">`;
99
+ if (ears === 'cat') {
100
+ svg += `<polygon points="15,45 10,10 45,15" fill="${headColor}" />
101
+ <polygon points="85,45 90,10 55,15" fill="${headColor}" />`;
102
+ } else if (ears === 'bear') {
103
+ svg += `<circle cx="20" cy="25" r="18" fill="${headColor}" />
104
+ <circle cx="80" cy="25" r="18" fill="${headColor}" />`;
105
+ } else if (ears === 'bunny') {
106
+ svg += `<ellipse cx="30" cy="15" rx="10" ry="30" fill="${headColor}" />
107
+ <ellipse cx="70" cy="15" rx="10" ry="30" fill="${headColor}" />`;
108
+ } else if (ears === 'alien') {
109
+ svg += `<line x1="30" y1="30" x2="15" y2="10" stroke="${headColor}" stroke-width="4" stroke-linecap="round" />
110
+ <circle cx="15" cy="10" r="6" fill="${detailColor}" />
111
+ <line x1="70" y1="30" x2="85" y2="10" stroke="${headColor}" stroke-width="4" stroke-linecap="round" />
112
+ <circle cx="85" cy="10" r="6" fill="${detailColor}" />`;
113
+ } else if (ears === 'elf') {
114
+ svg += `<polygon points="15,50 5,15 35,35" fill="${headColor}" />
115
+ <polygon points="85,50 95,15 65,35" fill="${headColor}" />`;
116
+ } else if (ears === 'robot') {
117
+ svg += `<rect x="10" y="35" width="10" height="25" rx="2" fill="${detailColor}" />
118
+ <rect x="80" y="35" width="10" height="25" rx="2" fill="${detailColor}" />
119
+ <circle cx="15" cy="40" r="2" fill="${headColor}" />
120
+ <circle cx="85" cy="40" r="2" fill="${headColor}" />
121
+ <circle cx="15" cy="50" r="2" fill="${headColor}" />
122
+ <circle cx="85" cy="50" r="2" fill="${headColor}" />`;
123
+ }
124
+ svg += `</g>`;
125
+
126
+ svg += `<g class="quirks-breathe">`;
127
+ // Head
128
+ if (headShape === 'circle') {
129
+ svg += `<circle cx="50" cy="50" r="35" fill="${headColor}" />`;
130
+ } else if (headShape === 'squircle') {
131
+ svg += `<rect x="15" y="15" width="70" height="70" rx="25" fill="${headColor}" />`;
132
+ } else if (headShape === 'blob') {
133
+ svg += `<path d="M 50 15 C 75 15 85 35 85 50 C 85 75 70 85 50 85 C 25 85 15 70 15 50 C 15 25 25 15 50 15 Z" fill="${headColor}" />`;
134
+ } else if (headShape === 'triangle') {
135
+ svg += `<polygon points="50,20 85,80 15,80" fill="${headColor}" />`;
136
+ } else if (headShape === 'hexagon') {
137
+ svg += `<polygon points="50,15 80,32 80,68 50,85 20,68 20,32" fill="${headColor}" />`;
138
+ } else if (headShape === 'diamond') {
139
+ svg += `<polygon points="50,10 85,50 50,90 15,50" fill="${headColor}" />`;
140
+ } else if (headShape === 'star') {
141
+ svg += `<polygon points="50,10 58,35 85,35 63,52 72,80 50,62 28,80 37,52 15,35 42,35" fill="${headColor}" />`;
142
+ } else if (headShape === 'cloud') {
143
+ 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
+ }
145
+
146
+ // Eyes
147
+ svg += `<g class="quirks-blink">`;
148
+ if (eyes === 'normal') {
149
+ svg += `<circle cx="35" cy="45" r="6" fill="#111" />
150
+ <circle cx="65" cy="45" r="6" fill="#111" />`;
151
+ } 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" />`;
154
+ } 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" />`;
157
+ } 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" />`;
160
+ } 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" />`;
166
+ } 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" />`;
170
+ } 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" />`;
173
+ } 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" />`;
176
+ } 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" />`;
179
+ }
180
+ svg += `</g>`;
181
+
182
+ // Mouth
183
+ 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>`;
185
+ } 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>`;
188
+ } 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>`;
190
+ } 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>`;
192
+ } 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>`;
196
+ } else if (mouth === 'o') {
197
+ svg += `<g class="quirks-talk"><circle cx="50" cy="70" r="8" fill="#111" /></g>`;
198
+ } 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>`;
200
+ } 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>`;
203
+ } 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>`;
209
+ }
210
+
211
+ // Accessory (face details)
212
+ 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" />`;
219
+ } 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" />`;
222
+ } 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" />`;
224
+ } 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" />`;
227
+ }
228
+
229
+ // Cheeks
230
+ 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" />`;
233
+ } 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" />`;
236
+ }
237
+
238
+ svg += `</g>`;
239
+
240
+ // Headwear (on top of everything)
241
+ svg += `<g>`;
242
+ if (headwear === 'hat') {
243
+ svg += `<rect x="25" y="5" width="50" height="15" rx="3" fill="${detailColor}" />
244
+ <rect x="20" y="18" width="60" height="6" rx="2" fill="${detailColor}" />`;
245
+ } else if (headwear === 'crown') {
246
+ svg += `<polygon points="25,22 30,5 40,15 50,2 60,15 70,5 75,22" fill="#ffd700" />
247
+ <circle cx="30" cy="18" r="3" fill="#e74c3c" />
248
+ <circle cx="50" cy="12" r="3" fill="#3498db" />
249
+ <circle cx="70" cy="18" r="3" fill="#2ecc71" />`;
250
+ } else if (headwear === 'horns') {
251
+ svg += `<path d="M 25 30 Q 15 15 20 5" stroke="${detailColor}" stroke-width="6" fill="none" stroke-linecap="round" />
252
+ <path d="M 75 30 Q 85 15 80 5" stroke="${detailColor}" stroke-width="6" fill="none" stroke-linecap="round" />`;
253
+ } else if (headwear === 'headphones') {
254
+ svg += `<path d="M 15 50 Q 15 20 50 15 Q 85 20 85 50" stroke="${detailColor}" stroke-width="6" fill="none" />
255
+ <rect x="8" y="45" width="12" height="20" rx="4" fill="${detailColor}" />
256
+ <rect x="80" y="45" width="12" height="20" rx="4" fill="${detailColor}" />`;
257
+ } else if (headwear === 'bow') {
258
+ svg += `<ellipse cx="35" cy="15" rx="12" ry="8" fill="${detailColor}" />
259
+ <ellipse cx="65" cy="15" rx="12" ry="8" fill="${detailColor}" />
260
+ <circle cx="50" cy="15" r="6" fill="${detailColor}" />`;
261
+ }
262
+ svg += `</g>`;
263
+
264
+ svg += `</svg>`;
265
+ return svg;
266
+ }
267
+
268
+ // Export for both ES modules and CommonJS
269
+ if (typeof module !== 'undefined' && module.exports) {
270
+ module.exports = { generateAvatarSvg };
271
+ } else if (typeof window !== 'undefined') {
272
+ window.generateAvatarSvg = generateAvatarSvg;
273
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "quirkatar",
3
+ "version": "1.0.0",
4
+ "description": "A free, lightweight, zero-dependency procedural avatar generator. Create unique, quirky profile pictures from any seed string.",
5
+ "main": "avatar.js",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./avatar.js",
10
+ "require": "./avatar.cjs"
11
+ }
12
+ },
13
+ "files": [
14
+ "avatar.js",
15
+ "avatar.cjs",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "scripts": {
20
+ "build:cjs": "node -e \"const fs=require('fs'); const code=fs.readFileSync('avatar.js','utf8'); fs.writeFileSync('avatar.cjs', code.replace('export { generateAvatarSvg };', 'module.exports = { generateAvatarSvg };'))\""
21
+ },
22
+ "keywords": [
23
+ "avatar",
24
+ "generator",
25
+ "procedural",
26
+ "svg",
27
+ "random",
28
+ "profile",
29
+ "identicon",
30
+ "placeholder",
31
+ "react",
32
+ "vanilla-js",
33
+ "lightweight",
34
+ "zero-dependency"
35
+ ],
36
+ "author": "Nitty-Gritty-Design",
37
+ "license": "MIT",
38
+ "homepage": "https://nitty-gritty-design.github.io/quirkatar-avatar-generator/",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/Nitty-Gritty-Design/quirkatar.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/Nitty-Gritty-Design/quirkatar/issues"
45
+ },
46
+ "sideEffects": false
47
+ }