Le pitch
Star Wars Avatar est un créateur d’avatar en ligne pour l’univers Star Wars. On choisit parmi 4 espèces (Humain, Twi’lek, Zabrak, Mirialan) et des centaines d’éléments de personnalisation pour créer son personnage, puis on l’exporte en PNG ou on le partage via URL.
C’est l’un des premiers créateurs d’avatars Star Wars en ligne — et il a été codé trois fois, de trois manières différentes.
Essayer : starwarsavatar.com
Pourquoi ce projet
En septembre 2016, j’ai contacté Marc Murera, designer et illustrateur derrière le compte @starwarsinfographic. J’aimais son travail graphique et je lui ai proposé une collaboration : créer un générateur d’avatar Star Wars pour la sortie du film “Rogue One: A Star Wars Story” en décembre 2016.
Il a accepté. Il fournissait les assets graphiques, je m’occupais du code.
C’était mon premier projet collaboratif avec un designer.
Trois versions, trois époques
Ce qui rend ce projet particulier, c’est son histoire technique :
Version 1 — jQuery (2016) L’objectif était de sortir vite pour coïncider avec Rogue One. jQuery était le standard de l’époque, cela fonctionnait.
Version 2 — Vanilla JS (2018) Deux ans plus tard, jQuery commençait à peser. J’ai refait l’application en JavaScript pur pour éliminer cette dépendance et moderniser le code.
Version 3 — React + TypeScript (2025) Après avoir réalisé Dice Roller et Grid4, je voulais vérifier si j’étais capable de migrer une application legacy vers React. Star Wars Avatar était le candidat idéal : un projet que je connaissais par cœur, avec une complexité réelle (composition SVG, gestion de couleurs, export d’images).
Chaque version a été une réécriture complète, pas une migration progressive. Trois approches différentes pour le même problème. Mais le style visuel, lui, n’a pas changé — Marc Murera avait fourni les assets graphiques dès 2016, et ils sont restés les mêmes à travers les trois versions. Ce qui a changé, c’est tout le reste : l’architecture, la gestion d’état, la performance, le système de couleurs.
Le défi technique : composition SVG et couleurs
Un avatar est composé de dizaines d’éléments SVG superposés : tête, yeux, nez, bouche, cheveux, vêtements, accessoires… Chaque élément doit s’aligner parfaitement avec les autres.
Les problèmes rencontrés :
- Alignement des éléments — Certains SVG ne se calaient pas correctement. Il a fallu beaucoup d’ajustements manuels, élément par élément.
- Cohérence des couleurs — La couleur de peau doit se propager sur tous les éléments concernés (visage, cou, oreilles…). Idem pour les cheveux. J’ai créé un système de “groupes de couleurs” : chaque élément SVG porte des classes CSS (
col-obj,skin-obj,hair-obj,shadow-obj,light-obj…) qui déterminent quelle couleur il reçoit. Quand l’utilisateur change la couleur de peau, un service JavaScript parcourt tous les éléments du groupe et modifie leurstyle.filldirectement dans le DOM — avec des variantes calculées automatiquement (ombre à -5%, lumière à +5%, ombre forte à -10%, lumière forte à +10%). C’est du JavaScript pur, pas du CSS variables — parce que les SVG sont injectés dynamiquement et que les variables CSS ne se propagent pas facilement dans des SVG chargés à la volée. - Casques et visibilité — Quand on met un casque, certains éléments doivent disparaître (cheveux, oreilles…). Chaque casque SVG contient des classes qui déclarent ses règles de visibilité (
no-hair-rear,no-ears,no-glasses…). Un service dédié lit ces classes, puis appliquedisplay: noneaux éléments concernés. Certains casques utilisent aussi desclipPathpour masquer partiellement les cheveux plutôt que de les supprimer complètement. - Performance — Calculer les variations de couleur sur des dizaines de SVG peut bloquer l’interface. Ces calculs et la génération d’avatars aléatoires sont déportés dans un Web Worker.
Architecture (v3)
La version React suit une séparation claire :
Services (métier pur) :
svgCompositionService— Injection dynamique des SVG, cache, application des couleurs multi-coucheshelmetVisibilityService— Gestion de la visibilité des éléments sous les casques- Pool de canvas réutilisables pour l’export PNG
Web Worker :
- Calculs de variations de couleur déportés pour ne pas bloquer le thread principal
State (Zustand) :
- Sélection des éléments par catégorie
- Couleurs actives (peau, cheveux, yeux…)
- Persistance localStorage
UI :
- Sélecteurs par catégorie avec scroll horizontal (CSS Scroll Snap, plus léger que Swiper)
- Preview en temps réel
- Actions : Randomize, Reset, Download PNG, Share URL
Stack technique
- React + TypeScript
- Vite comme bundler (avec SWC)
- Zustand pour le state management
- Tailwind CSS en mode CSS-first
- Web Workers pour les calculs de couleur
- SVG + Canvas API pour la composition et l'export
- PWA — installable, offline
- i18n maison (français/anglais)
- Persistance localStorage pour les avatars sauvegardés
- Compression — Brotli + Gzip sur les assets
- CSP — Content-Security-Policy avec hashes auto-générés
Évolutions
| Date | Version | Description |
|---|---|---|
| Déc 2025 | 3.0 | Refonte React — Migration complète vers React/TypeScript, Tailwind CSS, PWA |
| Juil 2018 | 2.0 | Refonte Vanilla JS — Suppression de jQuery, modernisation du code |
| Déc 2016 | 1.0 | Version initiale — Lancement en jQuery pour la sortie de Rogue One |
Bilan
Durée v3 : environ 2 mois (novembre — décembre 2025).
Ce que j’ai appris :
- Ce que la collaboration avec Marc m’a surtout appris : quand les assets graphiques sont donnés, le code n’est plus l’enjeu principal. L’enjeu, c’est de ne pas les trahir. Aligner 300 SVG sans casser l’intention visuelle de l’illustrateur — c’est un contrat tacite qui cadre toutes les décisions techniques.
- Migrer une application legacy vers React est faisable, mais demande de repenser l’architecture plutôt que de traduire ligne par ligne
- La composition SVG dynamique avec gestion des couleurs est plus complexe qu’il n’y paraît
Ce que je referais pareil :
- Réécrire plutôt que migrer progressivement — chaque version a bénéficié d’un regard neuf
- Séparer clairement les services métier de l’UI
Ce que je changerais :
- Difficile à dire. J’ai littéralement codé cette application trois fois de trois manières différentes. Je ne suis pas sûr de vouloir la refaire une quatrième fois.
- J’aimerais ajouter d’autres espèces aliens — Togruta, Rodien, Chiss… mais cela demanderait de nouveaux assets graphiques.
The pitch
Star Wars Avatar is an online avatar creator for the Star Wars universe. Choose from 4 species (Human, Twi’lek, Zabrak, Mirialan) and hundreds of customization elements to create your character, then export it as PNG or share it via URL.
It’s one of the first Star Wars avatar creators online — and it has been coded three times, in three different ways.
Try it : starwarsavatar.com
Why this project
In September 2016, I contacted Marc Murera, the designer and illustrator behind @starwarsinfographic. I loved his graphic work and proposed a collaboration: create a Star Wars avatar generator for the release of “Rogue One: A Star Wars Story” in December 2016.
He accepted. He provided the graphic assets, I handled the code.
It was my first collaborative project with a designer.
Three versions, three eras
What makes this project special is its technical history:
Version 1 — jQuery (2016) The goal was to ship fast to coincide with Rogue One. jQuery was the standard at the time, it worked.
Version 2 — Vanilla JS (2018) Two years later, jQuery was becoming a burden. I rebuilt the application in pure JavaScript to eliminate this dependency and modernize the code.
Version 3 — React + TypeScript (2025) After completing Dice Roller and Grid4, I wanted to verify if I could migrate a legacy application to React. Star Wars Avatar was the ideal candidate: a project I knew inside out, with real complexity (SVG composition, color management, image export).
Each version was a complete rewrite, not a progressive migration. Three different approaches to the same problem. But the visual style didn’t change — Marc Murera had provided the graphic assets back in 2016, and they remained the same across all three versions. What changed was everything else: architecture, state management, performance, the color system.
The technical challenge: SVG composition and colors
An avatar is composed of dozens of layered SVG elements: head, eyes, nose, mouth, hair, clothes, accessories… Each element must align perfectly with the others.
Problems encountered:
- Element alignment — Some SVGs didn’t align correctly. It required a lot of manual adjustments, element by element.
- Color consistency — Skin color must propagate to all relevant elements (face, neck, ears…). Same for hair. I created a “color groups” system: each SVG element carries CSS classes (
col-obj,skin-obj,hair-obj,shadow-obj,light-obj…) that determine which color it receives. When the user changes skin color, a JavaScript service traverses all elements of the group and modifies theirstyle.filldirectly in the DOM — with automatically calculated variants (shadow at -5%, light at +5%, strong shadow at -10%, strong light at +10%). It’s pure JavaScript, not CSS variables — because SVGs are dynamically injected and CSS variables don’t propagate easily into SVGs loaded on the fly. - Helmets and visibility — When wearing a helmet, some elements must disappear (hair, ears…). Each helmet SVG contains classes that declare its visibility rules (
no-hair-rear,no-ears,no-glasses…). A dedicated service reads these classes, then appliesdisplay: noneto the relevant elements. Some helmets also useclipPathto partially mask hair rather than removing it completely. - Performance — Calculating color variations on dozens of SVGs can block the interface. These calculations and random avatar generation are offloaded to a Web Worker.
Architecture (v3)
The React version follows a clear separation:
Services (pure business logic):
svgCompositionService— Dynamic SVG injection, caching, multi-layer color applicationhelmetVisibilityService— Visibility management for elements under helmets- Reusable canvas pool for PNG export
Web Worker:
- Color variation calculations offloaded to avoid blocking the main thread
State (Zustand):
- Element selection by category
- Active colors (skin, hair, eyes…)
- localStorage persistence
UI:
- Category selectors with horizontal scroll (CSS Scroll Snap, lighter than Swiper)
- Real-time preview
- Actions: Randomize, Reset, Download PNG, Share URL
Tech stack
- React + TypeScript
- Vite as bundler (with SWC)
- Zustand for state management
- Tailwind CSS in CSS-first mode
- Web Workers for color calculations
- SVG + Canvas API for composition and export
- PWA — installable, offline
- i18n custom (French/English)
- localStorage persistence for saved avatars
- Compression — Brotli + Gzip on assets
- CSP — Content-Security-Policy with auto-generated hashes
Timeline
| Date | Version | Description |
|---|---|---|
| Dec 2025 | 3.0 | React rebuild — Complete migration to React/TypeScript, Tailwind CSS, PWA |
| Jul 2018 | 2.0 | Vanilla JS rebuild — Removed jQuery, modernized codebase |
| Dec 2016 | 1.0 | Initial version — Launched in jQuery for Rogue One release |
Takeaways
Duration v3: about 2 months (November — December 2025).
What I learned:
- What working with Marc actually taught me: when the graphic assets are given, the code isn’t the main challenge. The challenge is not betraying them. Aligning 300 SVGs without breaking the illustrator’s visual intent — it’s an unspoken contract that frames every technical decision.
- Migrating a legacy application to React is feasible, but requires rethinking the architecture rather than translating line by line
- Dynamic SVG composition with color management is more complex than it appears
What I’d do the same:
- Rewrite rather than migrate progressively — each version benefited from a fresh perspective
- Clearly separate business services from UI
What I’d change:
- Hard to say. I’ve literally coded this application three times in three different ways. I’m not sure I want to do it a fourth time.
- I’d love to add more alien species — Togruta, Rodian, Chiss… but that would require new graphic assets.