BLOG

Handwired : Transforme du code JS ordinaire en réseau de neurones, 1730 étoiles en 4 jours.

Kael Zhang
Démontage techniquecâblé manuellementProgrammableWebGPUTypeScriptProjet open source
广告 · Advertisement

Le 15 octobre, le développeur chenglou a créé le dépôt handwired sur GitHub. La première phrase du README est : « Transformez votre code JavaScript ordinaire en un réseau neuronal ; pas de formation nécessaire, le résultat est précis et entièrement différentiable ». Au 19 octobre, le dépôt avait 1730★, 143 forks, 9 problèmes ouverts. En 4 jours, il a atteint le niveau des milliers d’étoiles, avec chenglou seul ayant apporté 825 contributions. Le projet est licencié MIT, écrit en TypeScript, compilé avec bun, et la démonstration en ligne est disponible sur chenglou.me/handwired. Pour le lancer localement, il suffit de faire bun install && bun start. Intéressant est le parcours de diffusion : les deux contributions simultanées sur HN n’ont reçu que 5▲ et 4▲ respectifs, avec presque toute la popularité venant du côté GitHub. Ce texte s’inscrit dans la tradition de diviser en six sections.

I. Qui est-ce

L’auteur est chenglou, avec 1295 karma sur HN. Seulement les liens vers GitHub et Twitter sont fournis dans la biographie personnelle. Les informations communautaires indiquent qu’il est un développeur reconnu de l’écosystème React (auteur de React Motion, promoteur de ReasonML, ancien employé de Meta). Cette partie du contexte n’a pas été vérifiée séparément dans ce texte ; le reste est traité comme « développeur chenglou ».

Il y a une différence de temps dans le projet lui-même : le dépôt a été créé le 15 octobre, mais les archives expérimentales dans le dossier lab ont été écrites au début septembre, mentionnant des discussions de conception en juin et juillet. Un journal d’expérimentation mis à jour quotidiennement est disponible à la mi-septembre, indiquant que le développement a eu lieu bien avant sa publication. Les 825 contributions en 4 jours correspondent au rythme de mise à jour du dépôt public, mais ne représentent pas toute la durée de développement du projet. Il est plus logique de comprendre cela comme une « publication complète après une longue préparation ».

Mesures effectuées sur le clone local (clone léger, du 10 au 19 octobre) : 1542 fichiers dans le dépôt, dont 1101 fichiers ts, pour environ 275 000 lignes ; le dossier central src compte environ 18 000 lignes, le dossier d’expérimentation lab environ 235 000 lignes, les tests environ 22 000 lignes, et les six documents docs pour un total de 1 296 lignes. La distribution des types de fichiers est : ts 1101, json 151, md 141, txt 49, html 38, css 22. Ce n’est pas une démonstration d’idée au niveau de gist, mais un projet complet avec des documents, des tests, des tutoriels et un site web en ligne.

II. Analyse du mécanisme

L’approche de handwired : le programme est composé de conditions if-else, de boucles et de états. Il transforme ces éléments en une opération de « multiplication matricielle avec reglu » exécutée sur des nombres à virgule flottante.

Le README propose un exemple très clair. Une branche du type a > 4 ? 10 : 12 peut être réécrite en calcul arithmétique pur : d’abord, on calcule above4 = max(0, a-4) - max(0, a-5), où max(0, x) correspond à ReLU. Pour les nombres entiers a, above4 est égal à 1 si a > 4, sinon à 0. Ensuite, on calcule result = above4*10 + (1-above4)*12. Les deux branches sont calculées séparément, et one of them est choisi par above4. La comparaison et le choix de ces primitives de contrôle sont ainsi codés en addition, multiplication, soustraction et ReLU. Le même principe s’applique aux boucles et aux états : les états sont stockés dans une table source de type Float32Array, et à chaque itération, on lit la table, calcule et écrit le résultat.

L’étape suivante est la collecte : toutes les additions, multiplications et opérations ReLU sont regroupées en une grande opération matmul, qui est exécutée en JS ou avec WebGPU. Comme seuls ces deux primitives sont utilisées, le README stipule trois propriétés : tout le programme reste différentiable ; les poids sont manuscrits, sans besoin de entraînement ; les technologies d’optimisation matures de la GPU (fusion, sparsity, reordération) sont particulièrement efficaces pour ces primitives minimalistes.

Les définitions dans docs/execution-model.md sont beaucoup plus strictes que celles du README. Le programme est une série d’instructions de longueur fixe, avec seulement quatre types : affine (poids affine), reglu (left * max(0, gate), qui se réduit à ReLU lorsque gate est constant 1), boolean (mise en forme des scores de 0..1 en 0 ou 1 exacts), et index (lecture de table, c’est-à-dire extraction de valeurs de la table candidate selon floor(sous-référence)). Toutes les significations numériques sont écrites au niveau d’arrondi FP32, les termes affines sont arrondis un par un selon l’ordre de stockage, et le document exige explicitement que tout backend soit parfaitement cohérent avec levaluateur de référence à chaque bit.

La comparaison n’est pas seulement codée avec ReLU. emitCompare produit directement des bits exacts sur les opérateurs d’un réseau entier : pour a>=b sur un réseau de 1/64, il suffit de 2 opérations et 2 couches ; sans déclaration de réseau, une cascade symbolique est utilisée, avec 15 opérations et 14 couches pour a>=b dans la plage 0..16,384. La lecture par index dispose d’une méthode spécifique lowering, qui permet de lire une seule opération pour n’importe quel taille de table, avec des plages d’index limitées à ±2^24. Ce que vous écrivez n’est pas un tableau d’instructions, mais du « code de construction » : le JavaScript normal est exécuté une seule fois pendant la construction, et les macros emitMultiply et emitCompare sont déployées en instructions, sans aucune branche ou boucle pendant l’exécution.

La différentiabilité est également structurelle, pas simplement une mise à jour. Le gradient peut revenir le long de ces quatre instructions : le gradient d’affine est les coefficients, celui de reglu inclut la porte ReLU, et les opérations de mise en forme comme boolean sont désignées dans le document comme « rounding point ». Le backend peut réarranger librement les autres sommets, mais ces points doivent être assurés dans leur signification. Lors de la dérivation d’un index, le client doit explicitement choisir entre la stratégie zero ou backward-difference, sinon une erreur est rencontrée : la conception prévoit plutôt une erreur que de vous donner un gradient non défini en secret. Ces conventions font que « tout le programme étant différentiable » devient un contrat avec des limites.

Au-dessus encore, il y a le temps et l’animation. Le fichier host-timing.ts divise l’animation de la résistance en intervalles de 4 ms, et une matrice de pas 2×2 est pré-combminée en fonction de la rigidité et du amortissement. À chaque frame, l’hôte écrit cette matrice dans 4 cellules d’entrée, et le programme l’applique via emitApplySpringMatrix. En d’autres termes, même les animations de l’interface utilisateur, qui ne ressemblent pas du tout aux réseaux neuronaux, ont été compilées dans le même tableau numérique.

Troisième partie : Qualité de l’ingénierie

Le fichier src est divisé en quatre parties : runtime est le noyau de l’exécution, et les quatre fichiers sont cells, host-timing, numeric-ui-core et prefix-networks ; handwiredos est une démonstration de bureau complète, avec le gestionnaire de fenêtres, Photos, Notes et Files tous compilés en programmes numériques, environ 5500 lignes ; json-search compte environ 6200 lignes, c’est une démonstration de recherche ; learned-gallery est une démonstration liée à l’apprentissage. Le dossier tests contient 42 fichiers de test, couvrant la sémantique comparative, la collecte de données, la sémantique numérique, la matrice de résistance, etc. Un module json-search contient 184 tests, et le programme met environ 45 secondes pour les exécuter. Le dossier template contient un exemple d’enseignement avec des boutons de sélection, et le fichier docs/tutorial.md explique ligne par ligne, accompagné d’une implementation de référence écrite en JS, avec des tests pour vérifier que les deux sorties sont identiques. Dans les documents docs, il y a aussi patterns.md (484 lignes), behavior-choices.md, execution-model.md, etc., six documents, montrant une organisation très ordonnée dans l’ingénierie.

La démonstration handwiredOS mérite quelques mots de plus, car c’est la présentation la plus convaincante de toute la bibliothèque. La déplacement des fenêtres, le maximisation, le sélectionnement des photos par clic, le saut de ligne des textes, la mise à l’échelle de la liste virtuelle, toutes ces interactions ne se retrouvent pas dans le JS de l’hôte, mais sont compilées dans les programmes numériques : les tests de collision des fenêtres utilisent emitCompare sur une grille de 1/64 de pixel, et les coordonnées de la souris sont d’abord arrondies à la grille avant d’être envoyées au programme, donc chaque comparaison est exacte en termes de chiffres entiers. La liste virtuelle dans la fenêtre Files est simplement une liste de valeurs du programme lui-même, avec 64 emplacements DOM répartis par le programme. L’hôte est simplement chargé d’écrire les événements du navigateur dans le tableau source, d’exécuter une valeurisation une fois, et d’écrire la sortie de retour dans le DOM. En d’autres termes, un système de bureau fonctionnel est en réalité un calcul matriciel sur un tableau de nombres à virgule flottante.

Les chiffres de performance sont enregistrés dans lab/status.md, avec une précision au micronième de seconde. Le handwiredOS entier est compilé en 20,381 opérations actives, 230 couches, 45,436 poids de stockage, avec une valeurisation d’environ 133µs par fois, des frames inactifs d’environ 141µs, et des frames d’écriture de texte d’environ 282µs. Le README de json-search reconnaît que un seul aller-retour du GPU prend plus de 0,6ms, ce qui n’apporte aucun avantage pour un seul document, l’avantage se trouvant uniquement dans une situation où 100 000 documents sont exécutés en parallèle : chaque document effectue les mêmes opérations, sans lire les valeurs des autres, tout est examiné une fois.

Le README décrit trois cas d’utilisation (d’après les mots de l’auteur) : l’entraînement direct des poids de la rétroaction, selon les mots de l’auteur : « Que se passe-t-il alors ? On le sait après l’essai » ; l’entraînement end-to-end avec une neurale générale pour les programmes spécifiquement gelés, avec la question : « Le nanoGPT peut-il apprendre à la fois Shakespeare et votre interface utilisateur ? » ; et le JS traditionnel en un seul thread gagnant en parallélisme GPU, permettant des recherches JSON à l’échelle de 100 000 documents en quelques millisecondes. Tous les trois cas se terminent par une question, et l’attitude expérimentale est visible sur le visage de l’auteur.

Le rythme des contributions est un point d’observation : 825 contributions par personne en 4 jours. L’auteur ne mentionne aucune méthode de génération, mais les notes de révision du 3 septembre indiquent que l’auteur a analysé les conversations avec Claude en juin et juillet, ainsi que les discussions sur la conception de Codex en juin. Les révisions ont été soumises à trois groupes d’auditurs indépendants. Les documents sont présentés ici ; le jugement reste à la discrétion du lecteur.

Il y a aussi des preuves de loyauté : les notes de révision de septembre indiquent que « la découverte la plus préoccupante est également spécifique : certaines vérifications de véracité confirment que les implémentations partagent le même erreur ». Le projet note volontairement les failles de son propre système de test, plutôt que d’attendre que quelqu’un les découvre, ce qui est rare dans des dépôts célèbres.

IV. Comparaison avec la concurrence

Conceptuellement, handwired appartient à la vieille direction de « programmabilité numérique » : permettre aux flux de contrôle d’un programme ordinaire de exister sous une forme numérique programmable, en lien avec les idées des systèmes symboliques. Mais sa méthode a sa place : il ne s’agit pas d’entraîner un réseau pour approximer le programme, mais de compiler précisément un programme écrit à la main en une forme de neurale, où les poids ne sont pas le produit de l’entraînement, mais le programme lui-même. Les travaux similaires avec des interpréteurs numériques nécessitent généralement sacrifier la précision pour la programmabilité, en utilisant la souplesse à la place des branches ; handwired, à l’inverse, utilise des déclarations de portée, des réseaux entiers, des cascades symboliques pour fixer la précision, puis gagne en programmabilité. Les mots « sans entraînement, précis, programmable » s’appliquent tous trois, grâce à cette choix de direction de compilation. Cette comparaison se limite au niveau conceptuel, sans prétendre à l’avantage ou au défaut du projet concret.

V. Limites

Trois points. Premièrement, les bornes numériques : l’encodage des nombres entiers avec ReLU dépend de la portée des opérateurs définies, et le dépôt utilise des déclarations de portée et des bits de précision pour couvrir les cas, mais cela signifie que l’auteur doit maintenir lui-même la contractilité des nombres. Dans le monde des nombres flottants, les entrées sortant de la portée ne sont pas magiques, et les lectures d’index sortant de ±2^24 sont directement rejetées. Deuxièmement, l’expansion du volume : les calculs sont effectués des deux côtés des branches, et les opérateurs augmentent avec la complexité du programme. Il y a un exemple dans status.md : après l’intégration de Notes, HandwiredOS passe de 14 019 opérations actives à 20 381, et les poids de stockage passe de 31 359 à 45 436, avec une évaluation unique passant d’environ 75µs à environ 133µs. Un jeu de tablettes peut déjà effectuer 20 000 opérations, on peut imaginer l’ampleur des applications réelles. Troisièmement, la pratique : l’auteur qualifie lui-même le projet de laboratoire, et le README dit « Qu’est-ce que ça fait vraiment ? Voyons ! » C’est une preuve directe, sans qu’il soit nécessaire de l’exagérer.

VI. Comment le projeter

Regard d’abord la structure de popularité : HN avec deux posts, un total de 9 likes, GitHub avec 4 jours et 1730★. La notoriété provient des catégories trending et des réseaux sociaux, tandis que les forums technologiques ont à peine capté l’attention. Ce contraste indique que la plupart des gens sont attirés par l’idée de « code en réseau neuronal » ; le coût pour obtenir des likes est bien inférieur au coût de lecture du fichier execution-model.md. Les likes mesurent ici la notoriété du sujet, pas la pertinence.

Judge en fonction du public cible. Pour ceux qui veulent comprendre la programmation scalable, c’est une entrée de niveau manuel : lire six documents, exécuter des exemples avec le template, réaliser une démonstration locale avec une seule commande, en une demi-journée, ce qui est plus intuitif que la lecture de publications académiques. Pour ceux qui travaillent sur les calculs numériques GPU ou les compilateurs, ils peuvent apprendre la conception de macros, comment emitCompare et index read se transforment en instructions minimales lors de la construction, et comment utiliser rounding point pour préserver une grande liberté de réarrangement, des techniques qui peuvent être directement empruntées. Pour ceux qui cherchent des outils de production, ne venez pas maintenant : 9 problèmes ouverts, les interfaces peuvent changer à tout moment, l’auteur agit en tant qu’expérimentateur.

C’est un jouet de recherche ou un signal de paradigme ? Il est difficile de trancher en quatre jours. Mais ce qui est certain, c’est que l’objectif de compilation « le programme est une réseau » a été atteint par une personne avec 270 000 lignes de TypeScript, permettant au logiciel de fonctionner sur des systèmes de bureau et de rechercher des 100 000 documents en millisecondes. La complétude des documents et des tests est à la hauteur des likes. Cette réalisation elle-même vaut la peine d’être analysée en détail.

Sources d’information

  • Référentiel de repository : github.com/chenglou/handwired (README, docs/execution-model.md, docs/tutorial.md, docs/patterns.md, template/, src/json-search/README.md, lab/status.md, lab/reassessment-2026-09-03.md, clone local le 19-10 ; nombre de lignes et de fichiers statués par find/wc local)
  • Nombre de likes, forks, date de création, contributeurs et nombre de contributions : API GitHub (snap du 19-10, le nombre de likes est en évolution)
  • Enregistrement des contributions HN : id=50105849 (5▲), id=50105735 (4▲), snap du 19-10
  • Les parties non vérifiées dans les informations sur l’auteur sont marquées « informations communautiques » dans le texte principal.
广告 · Advertisement

Questions fréquentes

Qui est l'auteur de Handwired ?

L'auteur de Handwired est chenglou, un développeur reconnu dans l'écosystème React, auteur de React Motion, promoteur de ReasonML, et ancien employé de Meta.

Quelles sont les caractéristiques de Handwired ?

Handwired transforme du code JavaScript ordinaire en un réseau neuronal sans formation nécessaire, le résultat est précis et entièrement différentiable.

Comment peut-on utiliser Handwired ?

Pour utiliser Handwired, il suffit de compiler avec bun en exécutant la commande 'bun install && bun start'.