## Transformer un thème de lecteur musical en add-on runtime avec WASM et effets réactifs à l'audio
Le projet a débuté comme une tentative de réparation d'un thème défectueux pour un lecteur musical populaire basé sur Electron, mais il a évolué en une véritable extension runtime nommée ChromaSync — complète avec adaptation dynamique des couleurs, pulsations réactives à l'audio, Mode Zen et un cœur de calcul WebAssembly. Tout cela s'exécute par-dessus PulseSync — une plateforme pour personnaliser l'interface via JS/CSS — où l'auteur a heurté les limites du framework et a dû étendre la logique au-delà de CSS.
Architecture : Du thème à l'orchestrateur
ChromaSync a été initialement conçu comme un correctif visuel, mais il s'est rapidement développé en un système multicouche. Composants clés :
metadata.json— point d'entrée pour PulseSync ;handleEvents.json— description déclarative des paramètres ;script.js— orchestrateur central pour l'état et les effets ;style.css— styles de base et animations ;- Module WASM — cœur pour les calculs intensifs (pulsations, lissage) ;
- Couche backend — fonctionnalités réseau optionnelles.
Le pipeline PulseSync → handleEvents → script.js → DOM/CSS/WASM permet des changements dynamiques sans rechargement. Par exemple, lors du changement de piste, le script extrait les couleurs dominantes de la pochette d'album, les transmet aux variables CSS, et lance simultanément les pulsations réactives à l'audio via le cœur WASM. Tout en temps réel, sans latence notable.
Système de paramètres : 70 options sous contrôle
PulseSync permet aux utilisateurs d'ajuster les add-ons sans toucher au code. Chaque paramètre est défini dans une structure JSON avec un id obligatoire qui le lie à la logique runtime. Exemple de config pour le mode de transition d'arrière-plan :
{
"id": "backgroundTrackTransitionMode",
"name": "Withmena fona (trek)",
"description": "Effekt switching fonovoy oblozhki when smene treka: fade (plavnaya podmena) or slide (staraya uezzhaet vlevo, novaya vezzhaet right).",
"type": "selector",
"selected": "1",
"options": [
{
"name": "Fade (by umolchaniyu)",
"id": "fade"
},
{
"name": "Slide (left/right)",
"id": "slide-horizontal"
}
],
"defaultParameter": "fade"
}
Avec 70 paramètres de ce type, la normalisation de l'état devient essentielle. script.js s'en charge : il collecte les valeurs, les valide, les convertit en un objet d'état unifié et les applique à l'UI. Sans cette couche, le système serait ingérable — surtout avec des paramètres interdépendants, comme le Mode Zen qui désactive automatiquement les particules et atténue l'intensité des pulsations.
Évolution de script.js : De 700 à plus de 8 000 lignes
script.js a commencé comme un simple fichier de 700 lignes. Il fait maintenant plus de 8 000 lignes — non pas du bloat, mais la coordination de sous-systèmes en constante expansion. Il agit comme orchestrateur :
- Récupère et normalise les paramètres de PulseSync ;
- Suit l'état du lecteur (piste, progression, plein écran) ;
- Gère les éléments DOM et variables CSS ;
- Lance/arrête les effets réactifs à l'audio ;
- Appelle les fonctions WASM pour les calculs mathématiques intensifs ;
- Gère les événements souris/clavier pour le parallax et l'interactivité.
Exemple précoce : parallax d'arrière-plan basé sur le mouvement de la souris :
const handleParallax = (event) => {
const offsetX = event.clientX / window.innerWidth - 0.5;
const offsetY = event.clientY / window.innerHeight - 0.5;
const posX = `calc(50% + ${offsetX * 25}px)`;
const posY = `calc(50% + ${offsetY * 25}px)`;
requestAnimationFrame(() => {
bgLayer1.style.backgroundPosition = `${posX} ${posY}`;
bgLayer2.style.backgroundPosition = `${posX} ${posY}`;
});
};
Plus tard, des fonctionnalités avancées ont émergé — comme l'extraction dynamique de palettes de couleurs à partir des pochettes de pistes :
const applyChameleonStyles = (palette, sourceChoice) => {
if (settings.useCustomAccentColor?.value) {
document.documentElement.style.setProperty(
'--accent-color',
settings.customAccentColor?.value || '#8a63b3'
);
return;
}
const swatch = palette?.[sourceChoice] || palette?.Vibrant;
if (!swatch) return;
document.documentElement.style.setProperty('--accent-color', swatch.getHex());
};
WASM : Quand JavaScript ne suit plus
Le tournant est arrivé quand les pulsations ont évolué d'une simple animation vers un système intensif en calculs avec plusieurs modes, lissage et déclencheurs. JavaScript ne gérait plus les exigences de performance — surtout sur les appareils bas de gamme. Solution : déplacer le cœur vers Rust/WASM.
Chargement du module :
async function loadPulseWasm() {
const res = await fetch('/api/chromasync/rust', { cache: 'no-store' });
const buf = await res.arrayBuffer();
const { instance } = await WebAssembly.instantiate(buf);
return instance.exports;
}
Exemple de fonction Rust exportée :
#[no_mangle]
pub extern "C" fn c2(prev: f32, current: f32, weight: f32) -> f32 {
prev * weight + current * (1.0 - weight)
}
WASM gère :
- Calcul en temps réel de l'amplitude des pistes ;
- Transitions fluides d'image à image ;
- Optimisation du taux de rafraîchissement d'animation ;
- Isolation de la logique lourde du thread UI principal.
Résultat : 60 FPS stables même avec des effets actifs et des filtres complexes comme la pixelisation ou le bruit.
Enseignements clés
- PulseSync — plateforme pour transformer des thèmes en add-ons runtime complets sans accès au code source du lecteur.
- WASM est crucial pour les effets réactifs à l'audio : il offre des performances que JavaScript pur ne peut égaler.
- script.js — bien plus qu'un script ; c'est l'orchestrateur qui lie paramètres, DOM, CSS et cœur de calcul.
- L'adaptation dynamique des couleurs à partir des pochettes utilise l'analyse d'images runtime et les variables CSS.
- L'architecture est scalable : nouveaux effets ajoutés comme modules indépendants sans casser la logique existante.
Optimisation : La beauté ne doit pas coûter de FPS
Chaque nouvel effet risque des chutes de performance. L'auteur met l'accent sur le profilage et l'optimisation :
requestAnimationFrameplutôt quesetTimeoutpour les animations ;- Débouncing des événements souris et scroll ;
- Chargement paresseux des modules lourds (ex. : WASM seulement à l'activation des pulsations) ;
- Désactivation des effets inutilisés en arrière-plan ;
- Mise en cache des résultats d'analyse des pochettes.
Les tests montrent une utilisation CPU sous 8–12 % sur des ordinateurs portables moyens même avec tous les effets activés (pulsations, particules, parallax, filtres). Cela garde l'UI réactive et les animations fluides comme du beurre.
L'avenir : Prochaines directions
L'auteur envisage plusieurs axes de développement :
- Intégration avec les API système pour le volume et les touches média ;
- Support des shaders utilisateur via WebGL ;
- Export/synchronisation cloud des paramètres entre appareils ;
- Effets s'adaptant automatiquement au genre musical (ex. : pulsations agressives pour l'EDM, transitions fluides pour le jazz).
Principe de base : pas de fonctionnalités pour le plaisir. Chaque ajout doit résoudre un vrai problème utilisateur ou améliorer l'expérience sans sacrifier les performances.
— Editorial Team
Aucun commentaire pour le moment.