Étude de cas – Création du doodle Google Stanisław Lem

Marcin Wichary
Marcin Wichary

Bonjour, (étrange) monde

La page d'accueil de Google est un environnement de codage fascinant. Elle s'accompagne de nombreuses restrictions difficiles: une attention particulière portée à la vitesse et à la latence, la nécessité de répondre à toutes sortes de navigateurs et de travailler dans diverses circonstances, et… oui, la surprise et le plaisir.

Je parle des doodles Google, ces illustrations spéciales qui remplacent parfois notre logo. Et bien que ma relation avec les stylos et les pinceaux ait longtemps eu cette saveur distinctive d'une ordonnance d'éloignement, je contribue souvent aux interactions.

Chaque doodle interactif que j'ai codé (Pac-Man, Jules Verne, Exposition universelle) et dont j'ai contribué à la création était à la fois futuriste et anachronique: de belles opportunités pour des applications utopiques de fonctionnalités Web de pointe… et un pragmatisme pragmatique de la compatibilité multinavigateur.

Nous apprenons beaucoup de chaque doodle interactif. Le mini-jeu Stanisław Lem récent n'a pas fait exception, avec ses 17 000 lignes de code JavaScript qui ont essayé de nombreuses choses pour la première fois dans l'histoire des doodles. Aujourd'hui, je souhaite partager ce code avec vous. Vous y trouverez peut-être quelque chose d'intéressant ou vous pourrez me signaler mes erreurs. Je vais vous en parler un peu.

Afficher le code du doodle Stanisław Lem »

N'oubliez pas que la page d'accueil de Google n'est pas un lieu de démonstration technologique. Avec nos doodles, nous souhaitons célébrer des personnes et des événements spécifiques, et nous voulons le faire en utilisant les meilleurs éléments artistiques et les meilleures technologies que nous puissions mobiliser. Mais nous ne célébrons jamais la technologie pour la technologie. Cela signifie examiner attentivement toute partie du HTML5 largement compris qui est disponible et déterminer si elle nous aide à améliorer le croquis sans le distraire ni l'éclipser.

Voyons donc quelques-unes des technologies Web modernes qui ont trouvé leur place (et d'autres qui ne l'ont pas) dans le doodle Stanisław Lem.

Graphiques via DOM et canevas

Canevas est puissant et conçu pour exactement le type de choses que nous voulions faire dans ce croquis. Toutefois, certains des anciens navigateurs qui nous intéressaient ne le prenaient pas en charge. Et même si je partage littéralement un bureau avec la personne qui a mis en place un excellent excanvas, j'ai décidé d'opter pour une autre méthode.

J'ai créé un moteur graphique qui élimine les primitives graphiques appelées "rects", puis les affiche à l'aide de canevas ou de DOM si le canevas n'est pas disponible.

Cette approche présente des défis intéressants. Par exemple, déplacer ou modifier un objet dans le DOM a des conséquences immédiates, tandis que pour le canevas, il existe un moment spécifique où tout est dessiné en même temps. (J'ai décidé de n'avoir qu'un seul canevas, de le vider et de dessiner à partir de zéro à chaque frame. D'une part, trop de pièces mobiles, et d'autre part, pas assez de complexité pour justifier le fractionnement en plusieurs canevas qui se chevauchent et leur mise à jour sélective.)

Malheureusement, passer au canevas n'est pas aussi simple que de dupliquer les arrière-plans CSS avec drawImage(): vous perdez un certain nombre de choses qui sont fournies sans frais lorsque vous assemblez des éléments via le DOM, en particulier la superposition avec les indices Z et les événements de souris.

J'ai déjà supprimé l'indice Z avec un concept appelé "plans". Le croquis a défini un certain nombre de plans (du ciel très loin derrière, au pointeur de la souris devant tout) et chaque acteur du croquis devait décider à quel plan il appartenait (de petites corrections plus/moins dans un plan étaient possibles à l'aide de planeCorrection).

Lors du rendu via le DOM, les plans sont simplement traduits en indice Z. Toutefois, si nous effectuons un rendu via un canevas, nous devons trier les rectangles en fonction de leurs plans avant de les dessiner. Étant donné que cette opération est coûteuse à chaque fois, l'ordre n'est recalculé que lorsqu'un acteur est ajouté ou lorsqu'il passe à un autre plan.

Pour les événements de souris, j'ai également effectué une abstraction… en quelque sorte. Pour le DOM et le canevas, j'ai utilisé des éléments DOM flottants complètement transparents supplémentaires avec un indice Z élevé, dont la fonction est uniquement de réagir aux survols/sorties de la souris, aux clics et aux pressions.

L'un des éléments que nous voulions essayer avec ce doodle était de briser le quatrième mur. Le moteur ci-dessus nous a permis de combiner des acteurs basés sur le canevas avec des acteurs basés sur le DOM. Par exemple, les explosions de la finale se trouvent à la fois dans le canevas pour les objets de l'univers et dans le DOM pour le reste de la page d'accueil de Google. L'oiseau, qui vole normalement et est coupé par notre masque dentelé comme n'importe quel autre acteur, décide de se tenir à l'écart pendant le niveau de prise de vue et s'assoit sur le bouton "Je me sens chanceux". Pour ce faire, l'oiseau quitte le canevas et devient un élément DOM (et vice-versa plus tard), ce qui, je l'espère, sera complètement transparent pour nos visiteurs.

Fréquence d'images

Connaître la fréquence d'images actuelle et réagir lorsqu'elle est trop lente (et trop rapide) était une partie importante de notre moteur. Étant donné que les navigateurs ne renvoient pas la fréquence d'images, nous devons la calculer nous-mêmes.

J'ai commencé à utiliser requestAnimationFrame, puis je suis revenu à l'ancienne méthode setTimeout si la première n'était pas disponible. requestAnimationFrame économise intelligemment le processeur dans certaines situations (bien que nous fassions une partie de cela nous-mêmes, comme expliqué ci-dessous), mais nous permet également d'obtenir un taux de rafraîchissement plus élevé que setTimeout.

Le calcul du frame rate actuel est simple, mais il est sujet à des changements drastiques. Par exemple, il peut chuter rapidement lorsqu'une autre application monopolise l'ordinateur pendant un certain temps. Par conséquent, nous calculons une fréquence d'images "dégressive" (moyenne) tous les 100 tic physiques et prenons des décisions en conséquence.

Quel genre de décisions ?

  • Si la fréquence d'images est supérieure à 60 FPS, nous la limitons. Actuellement, requestAnimationFrame sur certaines versions de Firefox n'a pas de limite supérieure à la fréquence d'images, et il n'est pas utile de gaspiller le processeur. Notez que nous plafonnons à 65 ips, en raison des erreurs d'arrondi qui font que la fréquence d'images est légèrement supérieure à 60 ips sur d'autres navigateurs. Nous ne voulons pas commencer à la limiter par erreur.

  • Si la fréquence d'images est inférieure à 10 images par seconde, nous ralentissons simplement le moteur au lieu de supprimer des images. C'est une proposition perdante, mais j'ai estimé que sauter des images de manière excessive serait plus déroutant que de simplement avoir un jeu plus lent (mais toujours cohérent). Il y a un autre effet secondaire intéressant : si le système ralentit temporairement, l'utilisateur ne ressentira pas de saut étrange alors que le moteur essaie de rattraper son retard. (J'ai procédé légèrement différemment pour Pac-Man, mais le taux de rafraîchissement minimal est une meilleure approche.)

  • Enfin, nous pouvons envisager de simplifier les graphismes lorsque le nombre de frames par seconde devient dangereusement bas. Nous ne le faisons pas pour le doodle de Lem, à l'exception du pointeur de la souris (plus d'informations ci-dessous), mais nous pourrions hypothétiquement supprimer certaines animations superflues pour que le doodle soit fluide, même sur les ordinateurs plus lents.

Nous avons également le concept de "tic" physique et de "tic" logique. Le premier provient de requestAnimationFrame/setTimeout. Le ratio dans le jeu normal est de 1:1, mais pour l'avance rapide, nous ajoutons simplement plus de ticks logiques par tick physique (jusqu'à 1:5). Cela nous permet d'effectuer tous les calculs nécessaires pour chaque "tic" logique, mais de ne désigner que le dernier comme celui qui met à jour les éléments à l'écran.

Analyse comparative

On peut supposer (et c'était le cas au début) que le canevas sera plus rapide que le DOM dès qu'il sera disponible. Ce n'est pas toujours vrai. Lors des tests, nous avons constaté qu'Opera 10.0 à 10.1 sur Mac et Firefox sur Linux étaient en fait plus rapides lors du déplacement d'éléments DOM.

Dans un monde idéal, le croquis effectuerait un benchmark silencieux de différentes techniques graphiques : éléments DOM déplacés à l'aide de style.left et style.top, dessin sur un canevas, et peut-être même éléments DOM déplacés à l'aide de transformations CSS3.

puis passez à celui qui offre le frame rate le plus élevé. J'ai commencé à écrire du code pour cela, mais j'ai constaté que, du moins, ma méthode de benchmarking était assez peu fiable et nécessitait beaucoup de temps. Ce temps nous est refusé sur notre page d'accueil. Nous accordons beaucoup d'importance à la rapidité et nous voulons que le doodle s'affiche instantanément et que le jeu commence dès que vous cliquez ou appuyez dessus.

En fin de compte, le développement Web se résume parfois à devoir faire ce que vous devez faire. J'ai regardé derrière moi pour m'assurer que personne ne me regardait, puis j'ai simplement codé en dur Opera 10 et Firefox en dehors du canevas. Dans une prochaine vie, je reviendrai en tant que balise <marquee>.

Économiser du processeur

Vous connaissez cet ami qui vient chez vous, regarde le final de la saison de Breaking Bad, vous gâche la fin et la supprime de votre DVR ? Vous ne voulez pas être ce type, n'est-ce pas ?

Donc, oui, c'est la pire analogie de tous les temps. Mais nous ne voulons pas non plus que notre doodle soit ce type de personne. Le fait que nous soyons autorisés à accéder à l'onglet du navigateur d'un utilisateur est un privilège. En monopolisant les cycles de processeur ou en distrayant l'utilisateur, nous serions un invité désagréable. Par conséquent, si personne ne joue avec le dessin (pas de pressions, de clics de souris, de mouvements de souris ni de pressions sur les touches), nous voulons qu'il s'endorme à terme.

Quand ?

  • après 18 secondes sur la page d'accueil (les jeux d'arcade appellent cela le mode attract)
  • après 180 secondes si l'onglet est sélectionné
  • au bout de 30 secondes si l'onglet n'est pas sélectionné (par exemple, si l'utilisateur a ouvert une autre fenêtre, mais qu'il regarde toujours le doodle dans un onglet inactif)
  • immédiatement si l'onglet devient invisible (par exemple, si l'utilisateur a basculé vers un autre onglet dans la même fenêtre, inutile de gaspiller des cycles si nous ne sommes pas visibles)

Comment savoir si l'onglet est actuellement sélectionné ? Nous nous attachons à window.focus et window.blur. Comment savoir si l'onglet est visible ? Nous utilisons la nouvelle API Page Visibility et réagissons à l'événement approprié.

Les délais d'inactivité ci-dessus sont plus souples que d'habitude. Je les ai adaptés à ce doodle particulier, qui comporte de nombreuses animations d'ambiance (principalement le ciel et l'oiseau). Idéalement, les délais avant expiration devraient être limités à l'interaction dans le jeu (par exemple, juste après l'atterrissage, l'oiseau pourrait signaler au doodle qu'il peut s'endormir maintenant), mais je ne l'ai pas implémenté au final.

Comme le ciel est toujours en mouvement, lorsque vous vous endormez et vous réveillez, le doodle ne s'arrête pas et ne se lance pas simplement. Il ralentit avant de s'arrêter, et inversement pour la reprise, en augmentant ou en diminuant le nombre de "tic" logiques par "tic" physique si nécessaire.

Transitions, transformations et événements

L'un des avantages du langage HTML a toujours été que vous pouvez l'améliorer vous-même: si quelque chose n'est pas assez bon dans le portefeuille régulier de HTML et de CSS, vous pouvez forcer JavaScript à l'étendre. Malheureusement, cela implique souvent de repartir de zéro. Les transitions CSS3 sont excellentes, mais vous ne pouvez pas ajouter de type de transition ni utiliser de transitions pour autre chose que pour styliser des éléments. Autre exemple: Les transformations CSS3 sont idéales pour le DOM, mais lorsque vous passez au canevas, vous êtes soudainement livré à vous-même.

C'est pour ces raisons, et d'autres, que Lem Doodle dispose de son propre moteur de transition et de transformation. Oui, je sais, les années 2000 ont appelé, etc. Les fonctionnalités que j'ai intégrées ne sont pas aussi puissantes que CSS3, mais tout ce que le moteur fait est cohérent et nous offre beaucoup plus de contrôle.

J'ai commencé par un système d'action (événement) simple : une chronologie qui déclenche des événements à l'avenir sans utiliser setTimeout, car à un moment donné, le temps de dessin peut se détacher du temps physique à mesure qu'il s'accélère (avance rapide), ralentit (faible fréquence d'images ou mise en veille pour économiser le processeur), ou s'arrête complètement (en attendant que les images aient fini de se charger).

Les transitions ne sont qu'un autre type d'actions. En plus des mouvements de base et de la rotation, nous acceptons également les mouvements relatifs (par exemple, déplacer un élément de 10 pixels vers la droite), les éléments personnalisés tels que les tremblements, ainsi que les animations d'images par image clé.

J'ai mentionné les rotations, qui sont également effectuées manuellement: nous avons des sprites pour différents angles pour les objets qui doivent être pivotés. La raison principale est que les rotations CSS3 et du canevas introduisaient des artefacts visuels que nous avons jugés inacceptables. De plus, ces artefacts variaient d'une plate-forme à l'autre.

Étant donné que certains objets qui pivotent sont attachés à d'autres objets qui pivotent (par exemple, la main d'un robot connecté au bras inférieur, qui est lui-même attaché à un bras supérieur pivotant), j'ai également dû créer une origine de transformation de fortune sous forme de pivots.

Tout cela représente une quantité de travail importante qui couvre finalement le terrain déjà couvert par HTML5. Toutefois, parfois, la compatibilité native n'est pas suffisante, et il est temps de réinventer la roue.

Gérer les images et les sprites

Un moteur ne sert pas seulement à exécuter le doodle, mais aussi à travailler dessus. J'ai partagé quelques paramètres de débogage ci-dessus. Vous trouverez le reste dans engine.readDebugParams.

Le spriting est une technique bien connue que nous utilisons également pour les croquis. Il nous permet d'économiser des octets et de réduire les temps de chargement, et facilite le préchargement. Toutefois, cela rend également le développement plus difficile : chaque modification des images nécessiterait un nouveau sprite (largement automatisé, mais toujours lourd). Par conséquent, le moteur est compatible avec l'exécution sur des images brutes pour le développement, ainsi que sur des sprites pour la production via engine.useSprites. Les deux sont inclus dans le code source.

Doodle Pac-Man
Les sprites utilisés par le doodle Pac-Man.

Nous permettons également de précharger des images au fur et à mesure et d'arrêter le dessin si les images n'ont pas été chargées à temps, avec une fausse barre de progression. (Faux, car malheureusement, même HTML5 ne peut pas nous dire dans quelle mesure un fichier image a déjà été chargé.)

Capture d&#39;écran du graphique de chargement avec la barre de progression truquée.
Capture d'écran du graphique de chargement avec la barre de progression truquée.

Pour certaines scènes, nous utilisons plusieurs sprites, pas tant pour accélérer le chargement à l'aide de connexions parallèles, mais simplement en raison de la limite de 3,5 millions de pixels pour les images sur iOS.

Où se situe le HTML5 dans tout cela ? Il n'y a pas beaucoup de détails ci-dessus, mais l'outil que j'ai écrit pour le spriting/le recadrage était une toute nouvelle technologie Web: canevas, blobs, a[download]. L'un des aspects intéressants du code HTML est qu'il subsume progressivement les tâches qui devaient auparavant être effectuées en dehors du navigateur. La seule chose que nous devions faire ici était d'optimiser les fichiers PNG.

Enregistrer l'état entre les parties

Les univers de Lem ont toujours semblé vastes, vivants et réalistes. Ses histoires commençaient généralement sans beaucoup d'explications, la première page commençant in medias res, et le lecteur devait se débrouiller.

Le Cyberiad n'y fait pas exception, et nous voulions reproduire ce sentiment pour le doodle. Nous commençons par essayer de ne pas trop expliquer l'histoire. Une autre grande partie est la randomisation, qui nous a semblé convenir à la nature mécanique de l'univers du livre. Nous utilisons de nombreuses fonctions d'assistance liées à la randomisation dans de nombreux endroits.

Nous voulions également améliorer la rejouabilité d'autres manières. Pour ce faire, nous devions savoir combien de fois le doodle avait été terminé auparavant. La solution technologique historiquement correcte est un cookie, mais cela ne fonctionne pas pour la page d'accueil de Google. Chaque cookie augmente la charge utile de chaque page. Et encore une fois, nous accordons beaucoup d'importance à la vitesse et à la latence.

Heureusement, HTML5 nous offre le stockage Web, facile à utiliser, qui nous permet d'enregistrer et de rappeler le nombre de lectures générales et la dernière scène lue par l'utilisateur, avec beaucoup plus de grâce que les cookies ne le permettraient jamais.

Que faisons-nous de ces informations ?

  • Nous affichons un bouton de lecture rapide, qui permet de passer les scènes cinématiques que l'utilisateur a déjà vues.
  • Nous affichons différents éléments N pendant la finale
  • nous avons légèrement augmenté la difficulté du niveau de tir ;
  • Nous affichons un petit dragon de probabilités easter egg d'une autre histoire lors de votre troisième partie et suivantes.

Plusieurs paramètres de débogage contrôlent ce comportement:

  • ?doodle-debug&doodle-first-run : faire comme si c'était la première exécution
  • ?doodle-debug&doodle-second-run : faire comme si c'était une deuxième exécution
  • ?doodle-debug&doodle-old-run : faire comme si c'était une ancienne course

Appareil tactile

Nous voulions que le doodle soit parfaitement adapté aux appareils tactiles. Les plus modernes sont suffisamment puissants pour que le doodle fonctionne très bien, et l'expérience du jeu via le tapotement est beaucoup plus amusante que par clic.

Des modifications immédiates de l'expérience utilisateur ont dû être apportées. À l'origine, le pointeur de la souris était le seul élément qui indiquait qu'une scène coupée/une partie non interactive se déroulait. Nous avons ensuite ajouté un petit indicateur en bas à droite pour ne pas avoir à nous fier uniquement au pointeur de la souris (car il n'existe pas sur les appareils tactiles).

Normale Occupé Cliquables Clics
En cours
Pointeur normal en cours de travail
Pointeur d&#39;occupation en cours de travail
Pointeur cliquable en cours de travail
Pointeur de travail en cours sur lequel l&#39;utilisateur a cliqué
Finale
Pointeur normal finalv
Pointeur d&#39;occupation final
Pointeur cliquable final
Pointeur de clic final
Les pointeurs de souris pendant le développement et les équivalents finaux.

La plupart des éléments fonctionnaient dès le départ. Toutefois, des tests d'utilisabilité rapides et impromptus de notre expérience tactile ont révélé deux problèmes: certaines des cibles étaient trop difficiles à appuyer et les pressions rapides étaient ignorées, car nous venions de remplacer les événements de clic de souris.

La présence d'éléments DOM transparents cliquables distincts m'a beaucoup aidé, car je pouvais les redimensionner indépendamment des visuels. J'ai ajouté une marge intérieure de 15 pixels supplémentaire pour les appareils tactiles et je l'ai utilisée chaque fois que des éléments cliquables ont été créés. (J'ai également ajouté une marge de 5 pixels pour les environnements de souris, juste pour faire plaisir à M. Fitts.)

Pour l'autre problème, je me suis juste assuré d'associer et de tester les gestionnaires de début et de fin de contact appropriés, au lieu de m'appuyer sur un clic de souris.

Nous utilisons également des propriétés de style plus modernes pour supprimer certaines fonctionnalités tactiles que les navigateurs WebKit ajoutent par défaut (mise en surbrillance par appui, accroche par appui).

Comment détecter si un appareil donné exécutant le doodle est compatible avec le toucher ? Lazily. Au lieu de le déterminer à priori, nous avons simplement utilisé notre QI combiné pour déduire que l'appareil est compatible avec le toucher… après avoir reçu le premier événement de démarrage tactile.

Personnaliser le pointeur de la souris

Mais tout n'est pas tactile. L'un de nos principes directeurs était de mettre autant d'éléments que possible dans l'univers du doodle. L'interface utilisateur de la petite barre latérale (avance rapide, point d'interrogation), l'info-bulle et même, oui, le pointeur de la souris.

Comment personnaliser un pointeur de souris ? Certains navigateurs permettent de modifier le curseur de la souris en créant un lien vers un fichier image personnalisé. Cependant, cette méthode n'est pas bien prise en charge et est également quelque peu restrictive.

Si ce n'est pas cela, qu'est-ce que vous proposez ? Pourquoi ne pas faire du pointeur de la souris un autre acteur du doodle ? Cette méthode fonctionne, mais comporte un certain nombre de mises en garde, en particulier:

  • Vous devez pouvoir supprimer le pointeur de souris natif.
  • vous devez être assez doué pour synchroniser le pointeur de votre souris avec le "vrai"

La première est délicate. CSS3 permet cursor: none, mais ce n'est pas non plus compatible avec certains navigateurs. Nous avons dû faire preuve de souplesse: utiliser un fichier .cur vide comme solution de secours, spécifier un comportement concret pour certains navigateurs et même coder en dur d'autres éléments en dehors de l'expérience.

L'autre est relativement anodin en apparence, mais comme le pointeur de la souris n'est qu'une autre partie de l'univers du dessin, il héritera également de tous ses problèmes. La plus grande ? Si la fréquence d'images du dessin est faible, la fréquence d'images du pointeur de la souris sera également faible. Cela a des conséquences désastreuses, car le pointeur de la souris, étant une extension naturelle de votre main, doit être réactif quoi qu'il arrive. (Les personnes qui ont utilisé Commodore Amiga dans le passé hochent maintenant la tête vigoureusement.)

Une solution quelque peu complexe à ce problème consiste à dissocier le pointeur de la souris de la boucle de mise à jour régulière. Nous l'avons fait dans un univers parallèle où je n'ai pas besoin de dormir. Une solution plus simple pour ce problème ? Il suffit de revenir au pointeur de souris natif si la fréquence d'images glissante passe en dessous de 20 FPS. (C'est là que la fréquence d'images glissante est utile. Si nous réagissions au débit d'images actuel et qu'il oscillait autour de 20 FPS, l'utilisateur verrait le pointeur de souris personnalisé se cacher et s'afficher en permanence.) Nous en arrivons donc à:

Plage de fréquence d'images Comportement
> 10 fps Ralentissez le jeu pour éviter de perdre des images.
10 à 20 FPS Utilisez un curseur de souris natif plutôt qu'un curseur personnalisé.
20 à 60 FPS Fonctionnement normal.
> 60 fps Limitez la fréquence d'images afin qu'elle ne dépasse pas cette valeur.
Résumé du comportement dépendant de la fréquence d'images.

Et le pointeur de la souris est sombre sur un Mac, mais blanc sur un PC. Pourquoi ? Parce que les guerres de plates-formes ont besoin de carburant, même dans les univers fictifs.

Conclusion

Ce n'est pas un moteur parfait, mais il n'essaie pas de l'être. Il a été développé en même temps que le doodle de Lem et est très spécifique à celui-ci. Ce n'est pas grave. "L'optimisation prématurée est la racine de tous les maux", comme l'a déclaré Don Knuth. Je ne pense pas qu'il soit judicieux d'écrire d'abord un moteur en isolation, puis de ne l'appliquer que plus tard. La pratique informe la théorie tout autant que la théorie informe la pratique. Dans mon cas, le code a été supprimé, plusieurs parties ont été réécrites encore et encore, et de nombreux éléments courants ont été remarqués après, plutôt qu'avant. Mais au final, ce que nous avons ici nous a permis de faire ce que nous voulions : célébrer la carrière de Stanisław Lem et les dessins de Daniel Mróz de la meilleure façon possible.

J'espère que ce qui précède vous éclairera sur certains des choix et compromis de conception que nous avons dû faire, et sur la façon dont nous avons utilisé HTML5 dans un scénario concret. Maintenant, jouez avec le code source, testez-le et dites-nous ce que vous en pensez.

Je l'ai fait moi-même. Ce qui suit était en ligne ces derniers jours, en comptant à rebours jusqu'aux premières heures du 23 novembre 2011 en Russie, premier fuseau horaire à voir le doodle de Lem. Une chose un peu loufoque, peut-être, mais comme les gribouillages, les éléments qui semblent insignifiants ont parfois une signification plus profonde. Ce compteur était vraiment un bon "test de stress" pour le moteur.

Capture d&#39;écran de l&#39;horloge de compte à rebours dans l&#39;univers de Lem doodle.
Capture d'écran de l'horloge de compte à rebours dans l'univers du doodle Lem.

C'est une façon de voir la vie d'un doodle Google : des mois de travail, des semaines de tests, 48 heures de cuisson, le tout pour un jeu que les utilisateurs jouent pendant cinq minutes. Chacune de ces milliers de lignes JavaScript espère que ces cinq minutes seront bien utilisées. Il vous suffit juste d'en profiter.