Crédits
- Code — NoRecess
- Graphismes — Ced
- Musique — Tom et Jerry
Crédits tels que listés sur Pouet.
Cette demo repose sur un seul effet : un player d’animations avancé. Il met en œuvre du delta-packing précis au pixel près, avec antialiasing. Il a fallu une version spéciale d’Exomizer (le packer), ainsi que l’écriture d’une toolchain maison côté PC pour générer les données. La demo a été construite avec le compilateur PhrozenC, et entièrement créée sous mon propre IDE.
L’algorithme expliqué
Phreaks est une demo bâtie autour d’un effet unique : un lecteur de séquences d’animation. Beaucoup de gens m’ont demandé d’expliquer tout le processus de création qui se cache derrière. Cet article présente les différentes étapes de développement traversées (et la logique qui les sous-tend). J’espère qu’il vous fera prendre conscience que derrière une demo se cache souvent une quantité de travail considérable — et non, ce n’est pas si simple à faire, même quand il s’agit en apparence d’un simple lecteur d’animation…
Écrit à l’époque de la sortie, et gardé tel quel.
L’origine

Tout est parti d’une production précédente, Phat 2. Cette demo comportait déjà un lecteur d’animation comme celui de Phreaks ; son principe était rapide et facile à implémenter. Malheureusement, le résultat était visuellement désagréable, parce qu’il reposait sur des blocs — la résolution était donc plutôt grossière.
Techniquement, chaque frame d’animation était stockée dans un buffer séparé. Les données de texture étaient en réalité une combinaison d’opcodes PUSH BC, PUSH DE, PUSH HL, PUSH AF et EXX. Chaque frame était compressée séparément avec le packer BitBuster V1.2 de Team Bomba. Le lecteur, sur Amstrad CPC, décompressait alors une frame de l’animation et exécutait les opcodes pour l’afficher à l’écran.
Ce qu’il fallait améliorer par rapport à Phat 2
À la sortie de Phat 2, beaucoup de gens m’ont demandé comment j’avais procédé. J’ai vite compris que mon approche — bonne, certes — ne poussait pas les limites. Il y avait de la marge. J’ai une règle personnelle pour les demos : ne jamais faire ou réutiliser deux fois le même effet. Je savais donc que le prochain terrain de progrès, c’était la gestion mémoire (pour stocker beaucoup plus de frames), mais aussi le visuel, qu’il fallait améliorer en montant la résolution (à l’époque, je pensais encore à une solution à base de blocs, mais avec des blocs plus petits). Et j’ai commencé à prototyper la chose…
Les données
Il me fallait d’abord des données. J’ai d’abord songé à tout faire de zéro : frame par frame, je pouvais produire des déformations de logo avec l’excellent outil graphique Paint.net. J’ai fait quelques essais, sans être convaincu, et je savais que les gens attendaient des personnages qui dansent. J’ai donc fini par chercher sur le net des données exploitables pour une demo. C’est Apple (et ses publicités) qui m’a sauvé la journée : grâce à eux, j’ai trouvé cette vidéo sur YouTube :
Sans être la meilleure vidéo que j’aie jamais vue, je me suis dit que ses différentes phases d’animation pouvaient donner un résultat intéressant. Je savais qu’il fallait m’y tenir et me concentrer sur la meilleure restitution possible sur Amstrad CPC.
Conversion des données brutes
Il m’a fallu un téléchargeur de vidéos YouTube. J’ai employé SaveVideoDownload, mais je ne le recommande pas et je ne mets pas de lien : cela ressemble au genre d’extensions de navigateur qui en font bien plus que ce qu’elles prétendent — je le soupçonne d’installer des spywares, entre autres. Il a néanmoins fait le travail, et j’ai obtenu la vidéo complète en .mpg.
Une fois la vidéo en main, il me fallait un extracteur de bitmaps : pour chaque frame de la vidéo .mpg, je voulais obtenir une liste de fichiers .bmp. J’ai utilisé VideoLAN Player (VLC) pour cela. Je ne me souviens plus des étapes exactes suivies dans le logiciel, mais je sais qu’il a fallu des réglages retors (et des codecs ?) pour que cela fonctionne comme voulu — la seule chose qui compte ici, c’est de savoir que l’extraction de bitmaps est possible avec lui.
J’ai ensuite fait le tri dans les frames — trier des milliers de bitmaps, c’est décidément un long travail !
J’ai employé l’outil IrfanViewer pour réduire chaque frame à 2 couleurs et les redimensionner en 768 x 205 (ma résolution « écran large » préférée sur CPC — ici en mode vidéo 2).
Enfin, j’ai chargé chaque frame dans Paint.net pour y appliquer un effet de flou. C’était une étape importante : les frames avaient été redimensionnées et venaient d’une compression MPEG d’origine, ce qui laissait, triste conséquence, de nombreux artefacts autour du personnage. Le flou arrondissait le personnage. Une fois appliqué, il ajoutait malheureusement de nouvelles couleurs à l’image, et il fallait donc de nouveau réduire à 2 couleurs — tout cela à la main (aucun traitement par lots n’était disponible dans l’outil pour cette opération), un travail vraiment pénible à mener (beaucoup de temps, et beaucoup de concentration).
Après des jours de travail (oui…), mes données étaient prêtes à passer dans mes propres outils de conversion…
Des bitmaps aux données d’animation (dans l’esprit de Phat 2)
Ma première approche a été la plus élémentaire : compresser tout un écran de données avec Exomizer, puis le décompresser à l’exécution. J’employais le mode vidéo 2 de l’Amstrad CPC, celui qui offre la meilleure résolution possible. Visuellement impressionnant sur Amstrad CPC, mais très lent et terriblement gourmand en RAM : cela prenait tout bonnement 3 banks complètes de RAM étendue (17 Ko x 3). Inutilisable en l’état, et j’ai d’abord cru qu’un lecteur d’animation dans une résolution aussi grande était hors de ma portée. Mais j’ai persévéré…
Des bitmaps aux données d’animation (à base de scanlines)
Je savais que je voulais au bout du compte une précision au pixel. J’ai donc vite pensé à employer des scanlines horizontales pour stocker mes données d’image. Pour chaque ligne de l’image, je stockais une liste de valeurs entières X1 et X2 (les bords d’une ligne), décrivant des lignes horizontales. Une fois compressé avec Exomizer, le résultat était bien meilleur que celui de l’algorithme précédent — deux fois meilleur en occupation mémoire, au final, mais toujours vraiment lent à l’exécution.
Overflow — qui m’a beaucoup soutenu pendant toute la fabrication de la demo — m’a conseillé à ce moment-là d’employer l’inversion de pixels. Avec cette astuce, je n’avais plus que les positions X1, X2 devenant une bascule entre pixel allumé et pixel éteint. Mieux côté mémoire, mais encore trop volumineux : environ 30 Ko pour une seule animation.
J’ai fait beaucoup de petits ajustements dans mon format de données pour gagner de la place, mais ce n’était décidément pas prêt pour la scène.
Des bitmaps aux données d’animation (avec du delta-packing, dans l’esprit de Phat 2)
Puis il y avait cette chose que tout le monde m’a signalée après la sortie de Phat 2 : le delta-packing. Le principe consiste à afficher la première frame de l’animation en entier, puis, pour la suivante, à ne changer l’état que des pixels qui ont changé. Pour y arriver, j’ai passé du temps à réécrire mes outils afin de générer les données correspondantes. J’ai décidé à ce moment-là de revenir à un effet à base de blocs, à la Phat 2. Le résultat, après 2 semaines de travail, était tout simplement épatant : c’était rapide, et la même séquence d’animation que j’utilisais depuis le début tenait sous les 17 Ko. À ce stade du développement, j’ai réalisé que j’étais capable de stocker quelque chose comme 2 fois le contenu de la demo Phat 2 dans la mémoire du CPC !
Mais mon but était de faire mieux que Phat 2 visuellement : il fallait maintenant penser à améliorer la résolution…
Des bitmaps aux données d’animation (delta-packing façon Phat 2, avec des améliorations)
J’ai alors entrepris d’améliorer l’algorithme en place. J’ai décidé de coder de façon particulière tous ces blocs visuellement pleins ou vides : disons qu’un groupe de blocs de 8x8 serait en réalité stocké comme un bloc de 1x1 sous un identifiant unique spécial. Pour chaque frame, j’ai aussi dressé une liste de sprites à afficher autour de tout le personnage « en blocs » dessiné à l’écran. J’ai introduit de nombreux ajustements pour générer des données mieux compressées par Exomizer, et les résultats étaient assez convaincants. La séquence d’animation qui prenait 3 banks de RAM au départ n’en prenait plus que 14 Ko pour le même visuel — pour moi, c’était impressionnant.
C’est le moment où j’ai eu clairement en tête le processus de création d’une demo bâtie sur cet algorithme. On était toujours en mode vidéo 2 de l’Amstrad CPC.
Réorganiser les données d’animation (avec une version d’Exomizer modifiée par Hicks/Vanity)
J’ai remarqué à cette période qu’Exomizer donnait de meilleurs résultats avec un seul gros buffer linéaire qu’avec plusieurs petits. Je voulais modifier l’implémentation d’Exomizer pour qu’elle gère le changement de buffer source à la volée. Imaginez le cas d’une séquence d’animation qui prend 30 Ko de mémoire une fois compressée : il serait bien de pouvoir ranger ce buffer dans les banks de RAM étendue séparées (qui font 17 Ko).
J’ai discuté avec Hicks/Vanity de ce besoin très particulier concernant Exomizer, et il a gentiment accepté d’écrire une telle routine. Une fois son travail reçu, et après quelques corrections de bugs, la routine a été pleinement intégrée à la demo. Comme prévu, les données ont encore diminué : l’animation de 14 Ko n’en prenait plus que 11 !
Des bitmaps aux données d’animation (le véritable algorithme de Phreaks, enfin !)
Les jours passaient — j’avais simplement besoin d’un peu de repos. Mais je continuais à réfléchir à une meilleure façon de réduire la taille des données. 11 Ko pour mon test unitaire, c’était acceptable, mais une demo serait difficilement faisable avec des données aussi grosses. Puis un jour, j’ai eu l’idée. Au fond, l’effet reste à base de blocs. Pour chaque frame, j’employais une liste de sprites pour décrire des blocs précis au pixel. Je me suis dit : pourquoi ne pas analyser toutes les images qui composent la séquence d’animation entière, et en tirer une liste de sprites partagée sur toute la durée de l’animation ? Je l’ai fait.
J’ai adapté mes outils en conséquence. J’ai eu du mal à trouver la résolution optimale des sprites : trop grands, et on se retrouvait avec une liste infinie de sprites différents ; trop petits, et il y avait trop d’indices de sprites à stocker dans les données. La taille optimale que j’ai trouvée est 2x5. Cette résolution ne me mettait pas à l’aise en mode vidéo 2 de l’Amstrad CPC, mais elle devenait soudain séduisante en mode vidéo 0, où un octet de VRAM représente 2 pixels.
Résultat final : pour ma séquence d’animation de test, 108 tiles uniques suffisaient. Cela me permettait de stocker les indices de tiles sur un seul octet, une bien bonne nouvelle également. Une fois de plus, il a fallu ajuster mon format de données pour obtenir de meilleurs résultats à la compression par Exomizer. Le résultat : l’animation n’occupait plus que 6 Ko de mémoire…
Les grands échecs, côté compression
Par-dessus tout ce travail, j’ai tenté d’introduire le retournement horizontal et vertical des tiles. J’avais 2 bits quelque part, au moment de référencer une tile, pour porter cette information. Cela produisait des données brutes plus petites, mais malheureusement une compression par Exomizer moins bonne que si je n’avais rien fait.
J’ai découvert aussi que tout le code consacré au traitement spécial des blocs pleins ou vides était, une fois compressé, moins efficace qu’un simple sprite. Bonne nouvelle côté mémoire, mais j’avais passé tellement de temps à peaufiner cela que j’ai été déçu d’avoir tant travaillé pour… rien.
Enfin, de tous les ajustements que j’ai dû manipuler, j’ai retenu que stocker des listes de pixels allumés/éteints n’était pas efficace : j’ai eu bien plus de succès en raisonnant en inversions de pixels — si un pixel est allumé, il s’éteint (et réciproquement).
Aller plus loin dans le rendu : l’anti-aliasing

Je pensais au départ que l’anti-aliasing serait évident pour tout le monde, mais il semble que peu de gens aient vraiment saisi l’astuce :) Cela mérite donc quelques explications.
Ce n’est pas un post-effet appliqué une fois la frame entièrement rendue. Ce serait très lent à exécuter et cela mangerait probablement tellement de mémoire que je préfère ne pas y penser :)
Voici donc le sale petit secret : comme décrit dans cet article, l’effet est à base de tiles. Ce n’est pas un hasard si j’ai choisi une largeur de 2 pixels pour les tiles. Par conséquent, pour une ligne horizontale de tile, il n’existe que 4 configurations possibles : PIXEL ÉTEINT + PIXEL ÉTEINT, PIXEL ALLUMÉ + PIXEL ÉTEINT, PIXEL ÉTEINT + PIXEL ALLUMÉ, PIXEL ALLUMÉ + PIXEL ALLUMÉ. Dans la demo Phreaks, cela se traduit respectivement par 0 + 0, 1 + 0, 0 + 1, 2 + 2 (0, 1 et 2 étant des indices de palette). 1 + 0 et 0 + 1 sont toujours employés en début et en fin de ligne. Réfléchissez-y : cela simule visuellement un effet d’anti-aliasing !
Aller plus loin dans le rendu : le mode inversé
Le mode inversé consiste à afficher l’animation avec tous les pixels retournés horizontalement. Cette astuce apporte une forme de variation à l’effet visuel. Dans la demo Phreaks, on l’obtient en retournant horizontalement toutes les tiles (en clair, PIXEL ALLUMÉ + PIXEL ÉTEINT devient PIXEL ÉTEINT + PIXEL ALLUMÉ, et PIXEL ÉTEINT + PIXEL ALLUMÉ devient PIXEL ALLUMÉ + PIXEL ÉTEINT — ce qui s’exécute très vite grâce au mode vidéo 0 de l’Amstrad CPC !). Il faut de plus, à l’affichage d’une ligne horizontale de tiles, inverser aussi la position (« X = largeur d’écran - X »).
Conclusion
Au bout du compte, il m’a fallu 4 mois de travail pour que la séquence d’animation fonctionne comme voulu. C’était assez grisant de croire le format de données définitif trouvé, puis de revenir quelques jours plus tard avec de nouvelles idées, meilleures.
Comme vous le voyez, la génération des données a été un gros morceau. C’est souvent ce que l’on ne réalise pas en regardant une demo. Je suis en fait plutôt fier de tout ce travail, y compris des échecs, et même en comptant toutes les étapes nécessaires pour arriver au résultat final. J’ai vraiment repoussé les limites de ce que je croyais pouvoir faire avec l’Amstrad CPC. Ce que je préfère, dans cette histoire, c’est que de telles données n’auraient pas pu être générées sur un Amstrad CPC d’époque : cela demande trop de calcul, trop de données — c’est vraiment 2011, dans mon esprit. Et c’est bien pour cela que rester sur l’Amstrad CPC est si intéressant…
J’espère que vous verrez désormais la demo Phreaks comme un vrai progrès dans le (petit) monde des demos CPC ! :)
YouTube :
Diaporama
Huit images, capturées depuis l’image disquette de la release.








Commentaires venus de Pouet
Les commentaires ci-dessous ont été laissés sur Pouet.net au sujet de Phreaks. Ils sont cités tels qu’ils ont été écrits. Recopiés depuis cette page le 17 août 2026.
Nice BASIC demo xD (No, really, guys, the demo is cool!)
— Devilmarkus, 2010-12-16 · rulez
Youtube link : http://www.youtube.com/watch?v=N7o4sDbSQmg
— norecess, 2010-12-16
Cool! Anims are slow. I like the antialiasing.
— Optimus, 2010-12-16 · rulez
meh
— kusma, 2010-12-16
Nice demo! But S.O.T.A. fans like me will always prefer dancing girls!
— ham, 2010-12-16
Nice job!
— Soundy, 2010-12-16 · rulez
technically cool i guess, designwise meh, music is annoying. Piggie
— shock__, 2010-12-16
Just to explain a little bit.. The major thing to retain in this prod is not the FX itself ; it’s relatively trivial and everyone with some Z80 knowledge would be able to accomplish the same. No, the real thing is the number of UNIQUE frames, which is 195. Think about it : Amstrad CPC has 128Kb, remove 40Kb for video-memory (with the special resolution I use), remove 8Kb for the music (and its player), remove another 6Kb for the sprites displayed on top of the FX, then remove once again another 5Kb for the background, another 5Kb for the font, another 11Kb for the code itself… Let’s calculate… we have 53Kb free. So the 195 frames are packed in 53Kb. 20 frames is super easy to manage ; 50 frames : you need a good data structure then.. 195 frames is kind of big numbers here. Hope you realize that :)
— norecess, 2010-12-17
Nice
— Buckethead, 2010-12-17 · rulez
195 frames or not, this is pretty boring. basically, what kusma said.
— StingRay, 2010-12-17
Afficher les 26 commentairesAfficher moins de commentaires
Actually, the prog displays a single effect during its whole demonstration but i really like the anti-aliasing of the frames. Design is nice; same goes for the tune. Congrats to all involved; you managed to release (another) nice product for CPC just before the fall of 2010! Well done guys!
— voxy, 2010-12-17 · rulez
Well done, you did it in the right time… What Stingray said ” it’s a pretty boring.” Graphics and music don’t suit this demo.
— Beb, 2010-12-17
The visuals are boring, maybe you should have taken some wacky swirls or zoomer instead. The music is very nice though.
— Exin, 2010-12-18 · rulez
well, why not?!
— ɧ4ɾɗվ., 2010-12-18 · rulez
for the player
— Shazz^TRSi, 2010-12-18 · rulez
The design is really… horrible. Ok for the perf, 195 frames in 53kb, but I must admit that “animations” are not my cup of tea (cf. Phat 2)…
— Hicks, 2010-12-18
The technical achievement is definitely worth a thumb up! The animations seem a bit disconnected from the music though.
— Rouquemoute, 2010-12-19 · rulez
Phreaks would have been an awesome demo if Condense hired a designer…
— toms, 2010-12-20
watchable
— comankh, 2010-12-20
+ generally positive feelings + nice packing - slow depacking & drawing for 4 mhz
— Skate, 2011-01-08 · rulez
video please?
— guardian ٩๏̯͡๏۶, 2011-01-08
@Guardian: Youtube.
— Gʀɪʍʍy, 2011-01-09
Nice demo.
— Octoate, 2011-03-21 · rulez
Models of the animation are not my kind of stuff but 195 frames of this size is not usual on CPC, I guess.
— Eldrik, 2012-04-15 · rulez
Thumb up for the technical achievement. However, it would be far better (beauty and speed) too see with a cleaner design. I’m looking forward to a new demo
— krusty, 2012-04-15 · rulez
good work norecess!
— guardian ٩๏̯͡๏۶, 2013-03-04 · rulez
