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.
- package/LICENSE +21 -0
- package/README.md +143 -0
- package/avatar.js +273 -0
- 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
|
+

|
|
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
|
+
}
|