charset.school
actif · depuis 2026Un site pour apprendre à encoder et décoder des caractères à la main : un bac à sable qui décompose la conversion à la frappe, et des exercices où c'est à nous de faire le travail, bit par bit.
- ▸Elixir 1.20 (OTP 29)
- ▸Phoenix 1.8, LiveView 1.2
- ▸SQLite (mode WAL), Ecto
- ▸Tailwind v4 via Vite
- ▸Gettext (EN à la racine, FR sous /fr)
- ▸Docker multi-stage, Bandit
- ▸site en ligne ↗
- ▸code source ↗
- ▸licence: MIT
Ce que ça fait
Deux modes. Le bac à sable d'abord : dix pages, une par encodage et par sens (UTF-8, UTF-16, UTF-32, Latin-1, Windows-1252). On tape un caractère ou une suite d'octets, et la conversion se décompose à la frappe : le code point, les bits utiles, les groupes de bits, les octets en hexa, l'endianness quand elle compte. Rien à valider, on regarde.
Les exercices font l'inverse : six modules (encoder et décoder en UTF-8, UTF-16, UTF-32) où c'est à nous de convertir. Chaque exercice est découpé en étapes, une petite question chacune : quel format, quel binaire, quels groupes de bits, quels octets, combien de bits utiles, quel offset. Le serveur valide au fur et à mesure, l'indice dépend du type d'erreur commise, et la réponse s'affiche au bout de trois essais.
La difficulté monte toute seule : cinq bonnes réponses d'affilée font passer au niveau suivant. En UTF-8 ça va de 1 à 4 octets, en UTF-16 et UTF-32 du BMP aux plans supplémentaires, donc aux paires de substitution.
Pas de compte. Tout est rattaché à un identifiant anonyme posé dans un cookie, et
le site est bilingue : anglais à la racine, français sous /fr.
Comment c'est fait
Phoenix et LiveView sur une base SQLite, et rien d'autre : pas d'API séparée, pas de frontend à part. Chaque réponse part au serveur, revient validée, la page se met à jour.
La solution ne descend jamais dans le navigateur. L'exercice est généré côté serveur, chaque étape garde sa réponse attendue, et le client ne reçoit que ce qu'il faut pour afficher le champ. Sans ça, la réponse se lirait dans l'onglet réseau.
Un niveau ne remplace pas les précédents, il les mélange : au niveau 4 en UTF-8, la génération pioche encore 10 % de caractères sur un octet, 30 % sur deux, 30 % sur trois et 30 % sur quatre. Ça garde les cas simples en rotation, et ça évite surtout que l'étape "quel format" devienne une réponse automatique.
Pourquoi
Tout le monde a déjà vu un é s'afficher é sans savoir à quel moment ça a
dérapé. Comprendre ce qui se passe demande de savoir découper un code point en
bits, et ça rentre mieux en le faisant à la main quelques dizaines de fois qu'en
lisant une table.
Le site existait déjà, en Kotlin et Spring Boot pour l'API et Nuxt pour le front, environ 18 000 lignes. Je l'ai réécrit en Phoenix avec un LLM, sur des specs écrites en détail, en relisant et corrigeant au fur et à mesure : la même méthode que pour Jellyfin BetaSeries .
Le résultat est nettement plus court, parce qu'Elixir tombe pile sur le domaine : un format d'encodage, c'est une suite de bits à des positions fixes, et c'est exactement ce que les bitstrings savent écrire.
# UTF-8 sur deux octets : le format est le motif
<<0b110::3, high::5, 0b10::2, low::6>>
Pas de masques, pas de décalages à la main, pas de commentaire pour expliquer ce que fait la ligne : le code ressemble à la table de la RFC. Le décodage se lit dans l'autre sens, en pattern matching. L'ancienne version est toujours sur une branche du dépôt, pour comparer.