Workerthread
De engine, materialen, water, vuur, lucht, dieren en gereedschap, en de zeven WebAssembly-kernels.
Game Tailors
Bron: Hoe we het kloppend houden
De teken-app waarin het papier echt is. Geen reclame. Geen accounts. Geen persoonsgegevens: alleen anonieme tellingen, en die kun je uitzetten.
Game Tailors · Schieweg 15Y-40, 2627 AN Delft (NL)
KvK 92437702 · VAT NL866044474B01
info@gametailors.com · +31 6 4227 5919
© 2026 Game Tailors
Hoe we het kloppend houden
Honing die vandaag op een tekening wordt gegoten, moet zich gedragen zoals gisteren. Achter Professor Oops zitten duizenden automatische controles die doen wat kinderen doen: verf gieten, een hamer laten neerkomen, een vuurtje stoken, wachten op schimmel. Daarna kijken ze naar de pagina. De beloften waar een gezin om geeft, zoals “geen enkel dier raakt ooit gewond” en “geen reclame”, zijn ook als controles opgeschreven, zodat het breken van een ervan de testrun laat mislukken.

Bij de release bevatte de code 270 testbestanden, met daarin 2.614 tests en 8.933 losse controles. Dat is 56 regels test op elke 100 regels spel. De volgende update heeft 353 bestanden en 60 regels op elke 100 volgende update. De tests groeiden sneller dan het spel: op de tweede dag van het bouwen waren ze 12% van de omvang ervan.
De meeste zijn niet de gebruikelijke kleine controles op één functie. Veel ervan leggen een echte pagina neer, gebruiken er een echt stuk gereedschap op, laten de simulatie een paar seconden draaien en lezen wat er gebeurde. Hun namen zijn zinnen die je aan de keukentafel zou kunnen nagaan: “een klap plet een tomaat maar geen banaan; een deegroller die hard neerkomt, laat een sinaasappel barsten maar geen appel”. Ongeveer twee op de drie testbestanden laten de echte engine stappen zetten.
In een test beweegt de simulatie in vaste stappen, meestal een zestigste seconde, nooit op de klok. Zo krijgt een drukke computer precies hetzelfde antwoord als een rustige. Een snelle laag laat de zevenhonderd of zo tests weg die langer dan een seconde duren, voor gebruik tijdens het werk. De volledige run voegt een typecontrole van elk bestand toe, een productiebuild en een run in de browser, in Chrome.
Hier is een naad, eerlijk getoond. De controles draaien op onze eigen computers. Er is geen aparte buildserver die een release weigert, dus een mislukte wijziging buiten het spel houden is een werkregel die we met de hand bewaken. Echte telefoons en tablets worden ook met de hand geprobeerd: de automatische runs gebruiken een Chrome op de computer, plus één test van het aankoopverloop op een iPhone-simulator.
Van 18 okt. tot 5 nov. groeide de broncode van het spel van 21,3 tot 119,5 duizend regels, en de tests van 2,5 tot 72,1 duizend.
| datum (2026) | broncode van het spel | tests |
|---|---|---|
| 18 okt. | 21,3 | 2,5 |
| 20 okt. | 35,7 | 7,7 |
| 22 okt. | 48,6 | 20,2 |
| 24 okt. | 71,6 | 33,9 |
| 26 okt. | 85,9 | 44,4 |
| 28 okt. | 86,1 | 45,5 |
| 30 okt. | 99,4 | 54,4 |
| 1 nov. | 106,9 | 60,7 |
| 3 nov. | 118,2 | 69,1 |
| 5 nov. | 119,5 | 72,1 |
Bron: Regels code van het spel en van de tests, op één versie per dag geteld; na 2 nov. zijn de getallen die van de volgende update.
De drukste lussen van de simulatie bestaan twee keer. Dat zijn de plekken waar smurrie stroomt, papier doorweekt, vuur zich verspreidt, een hand verf wegschuift en de lucht haar druk vindt. De ene kopie is leesbare TypeScript. De andere is gecompileerd naar WebAssembly met SIMD, zodat een goedkope tablet het bijhoudt. De tests draaien de leesbare kopie en de apps de gecompileerde, dus de tests zeggen alleen iets over de apps als de twee overeenkomen.
Drie lagen houden ze bij elkaar. Ten eerste noemt elke gecompileerde kernel precies welke functies en constanten hij kopieert, en de build bewaart een vingerafdruk van hun broncode. Verander één regel van een gekopieerde lus en de testrun mislukt, met de naam van de functie erbij, tot zijn tweeling is overgezet en iemand heeft vastgelegd dat hij het heeft nagekeken. Commentaar en witruimte tellen niet mee; de code wel.
Ten tweede draait elke kernel een reeks testpagina’s twee keer, met en zonder hemzelf, en elk veld van elke cel wordt vergeleken. Voor smurrie, vuur, de wegschuivende hand, zeep op melk en de drukoplossing van de lucht, elk apart getest met de andere kernels uit, moet het antwoord tot op de laatste bit kloppen. Dat lukt doordat ze hun sommen in dezelfde volgorde en met dezelfde precisie maken als de leesbare kopie. De kernels voor papier en voor de drijvende laag gebruiken voor de snelheid 32-bitsbanen, dus die worden in plaats daarvan aan een kleine marge gehouden: één deel op 100.000 voor papier, één op 10.000 voor de drijvende laag.
Ten derde draait één pagina met elk materiaal erop met alle kernels aan en met alle uit, op zes paginavormen, en die moet binnen een opgegeven marge overeenkomen. Als de kernels op een apparaat niet kunnen starten, valt het spel terug op de leesbare lussen, en een test controleert dat dat precies dezelfde smurrie en natheid geeft als de leesbare engine.
Twee naden, eerlijk getoond. Op één scheve pagina wijken zes cellen in de onderste rij tot 3,4 delen op 10.000 af tussen de papierkernel en zijn tweeling. De test is zo geschreven dat hij die mislukking verwacht, zodat hij mislukt op de dag dat iemand het oplost en die persoon dwingt er een gewone test van te maken volgende update. En bij lagere detailinstellingen doet de leesbare lucht een goedkopere drukoplossing, terwijl de gecompileerde lucht altijd de volledige doet. De pariteitstest zet ze eerst gelijk. In de apps is de wind bij die instellingen de vollere, met opzet.
Links de leesbare lus; rechts zijn kopie. Beide draaien de regel van het spel voor nat papier op dezelfde 64 × 48 cellen, met ongeveer elke seconde een druppel die landt. Cellen waar de kopie zelfs maar één bit van het origineel verschilt, worden rood. Een vingerafdruk verschijnt als zijn 32-bitsgetal. Waar je op let: met dezelfde sommen in dezelfde volgorde komen de twee vingerafdrukken precies overeen, voor altijd. Tel dezelfde getallen in een andere volgorde op, of doe de sommen met 32-bitsgetallen zoals SIMD-banen doen, en de vingerafdruk breekt meteen, al blijven de cellen heel dicht bij elkaar: honderdmiljoensten uit elkaar bij een andere volgorde, miljoensten bij 32-bitssommen. Daarom houdt het spel vijf kernels aan de bit, en papier en drijvende lagen aan een marge. De klok loopt 3× zo snel.
Vijf kernels moeten identiek zijn; papier mag 1e-5 verschillen, de drijvende laag 1e-4, de hele pagina 2e-3. Het bekende gat is 3,4e-4, en één afronding in 32 bits is ongeveer 6e-8.
| afronding | 5 exact | papier | bekend gat | drijvend | hele pagina | |
|---|---|---|---|---|---|---|
| afronding | 60 | |||||
| 5 exact | 1 | |||||
| papier | 10 000 | |||||
| bekend gat | 340 000 | |||||
| drijvend | 100 000 | |||||
| hele pagina | 2 000 000 |
Bron: Toegestane of waargenomen verschillen tussen elke gecompileerde lus en zijn leesbare tweeling. Bekend gat: 3,4e-4, gezien in zes cellen van één scheve pagina, vastgelegd als verwachte mislukking volgende update. “Afronding”: één afronding van een 32-bitsgetal, ongeveer 6e-8, ter vergelijking. De maten verschillen, dus vergelijk elk op zichzelf, niet onderling.
Sommige testpagina’s zijn vastgepind. Een drukke pagina wordt bewerkt door een hele reeks gereedschappen, en aan het eind worden de velden ervan teruggebracht tot een vingerafdruk van acht tekens. Als verf, water of olie op die pagina anders uitkomen, al is het maar in één cel met één bit van de velden die hij dekt, verandert de vingerafdruk en mislukt de test.
Gedrag verandert wel met opzet. Dan wordt de vingerafdruk bijgewerkt, en de notitie ernaast zegt wat er veranderde en waarom, vaak met wat de vingerafdruk zou zijn als het nieuwe gedrag uit stond. Over de hele levensduur van deze bestanden kwamen 108 van de 112 wijzigingen aan een vingerafdruk met een notitie in dezelfde wijziging. Op volgorde gelezen zijn de notities een logboek van de natuurkunde van het spel: “de korrels van een pigment rijden met de waterstroom mee naar een drogende rand”.
Een vingerafdruk zegt “zoals eerst”. Hij kan niet zeggen “juist”: daarvoor zijn de scènetests. En die controleren vooral gedrag dat een kind zou herkennen, geen getallen uit de echte wereld.
Hetzelfde idee, tot op de bit, bewaakt een aantal beloften die je per ongeluk makkelijk breekt:
Opgenomen spel, afgespeeld vanaf een bewaard moment, eindigt op precies dezelfde pagina. Zonder de opgenomen willekeurige getallen en klok gaat het een andere kant op, en dat controleert de test ook.
Een sprong in de tijd geeft dezelfde pagina bij drie detailniveaus, elk met een eigen werkbudget.
Het meetpaneel openen verandert niets op de pagina.
De beelddata die tegel voor tegel worden verstuurd, zijn precies wat het opnieuw inpakken van de hele pagina zou versturen.
Ongedaan maken met kleine verschillen zet precies terug wat hele kopieën deden.
Spiegel de pagina en de wind spiegelt mee, tot op de bit.
De eigen hash van de tests. Een kleine pagina verf, 40 × 30 cellen, teruggebracht tot een vingerafdruk met dezelfde FNV-1a-hash die de vastgepinde tests gebruiken. Eén cel, rood omcirkeld, verschilt al van de vastgepinde pagina met de kleinste stap die een 32-bitsgetal kan zetten, ongeveer één deel op tien miljoen. Waar je op let: het plaatje kan het niet laten zien, toch is de vingerafdruk een heel ander getal en mislukt de pin. Tik op de omcirkelde cel, of druk op Zet de laatst veranderde cel terug, en de pin slaagt; tik op een andere cel om die te veranderen.
De simulatie draait in een Web Worker, een aparte thread, zodat tekenen en aanraken soepel blijven terwijl de pagina wordt uitgerekend. De kant van het scherm ziet een kopie van wat hij moet tekenen. Die splitsing heeft één nare valkuil. Schermcode die rechtstreeks in de engine grijpt, werkt nog als alles op één thread draait, en doet stilletjes niets in de worker. “Het is vier keer gebeurd”, zegt de test die het nu tegenhoudt.
Die test leest de broncode van de schermkant met de TypeScript-compiler en somt elke waarde op die hij van de kant van de engine haalt. Elke waarde moet op een lijst staan met een reden: een constante, een pure functie, een klasse waarvan de kopie aan de schermkant leeft, of toestand die de twee met opzet delen. Een nieuwe laat de run mislukken met een bericht dat zegt waar hij hoort. De test controleert ook dat hij een nieuwe import opmerkt, zodat hij niet stilletjes blind kan worden.
Een run in de browser dekt wat code lezen niet kan. Die opent het echte spel twee keer in Chrome zonder venster, met de simulatie in haar worker en in dezelfde thread, en draait in beide dezelfde controles. Zand springt op een luidspreker. Schuifjes bereiken de simulatie. Een bewaard plaatje is een echte JPEG van 3200 pixels. Een kleurplaat laadt. Een reeks scènes draait: een vetbrand onder een deksel, olie op water, gevouwen deeg, een batterij die een lampje laat branden. De naam van elk gereedschap past onder zijn plaatje, in elke taal. Elke controle is er omdat dat ding “een keer stilletjes kapotging in de workermodus”.
De engine, materialen, water, vuur, lucht, dieren en gereedschap, en de zeven WebAssembly-kernels.
→ berichten. Elke aanraking, elk schuifje en elke knop wordt een aanroep met een naam die de worker afhandelt.
← replica. Een kopie van wat er getekend wordt: 47 enginevelden en 40 klassen bij de release.
← texturen. Alleen de veranderde tegels van de pagina, overgedragen, niet gekopieerd.
toegestane lijst. Waarden die de schermkant mag importeren, elk met een reden: 198 in de volgende update volgende update.
Menu’s en knoppen, het WebGL2-papier, de schilderingen en de dieren erop.
Bij elke run gecontroleerd door een test
Een import “werkt in dezelfde thread en doet stilletjes niets in de worker”.
Elke enginewaarde die de schermkant importeert, staat op een lijst met haar reden.
Bij elke run gecontroleerd door een test
“Bewaard, of opnieuw uitgerekend, of alleen voor het moment.”
Niets nieuws gaat stilletjes verloren bij ongedaan maken, bij opnieuw laden of onderweg naar het scherm.
Bij elke run gecontroleerd door een test
Een fout in de laadvolgorde “hield de app een keer tegen in de browser”.
Elke simulatiemodule, de engine en de gereedschappen laden eerst, alleen, in gewone Node.
Bij elke run gecontroleerd door een test
“Voorwerpen die in code getekend werden, kwamen steeds terug.”
Elke functie die vormen of tekst tekent, moet zeggen waarom ze geen voorwerp tekent: 92 bij de release.
Bij elke run gecontroleerd door een test
Een naam die op veel plekken staat, loopt uit elkaar.
De broncode, configuraties, native projecten en juridische pagina’s halen hem uit één instelling.
Vastgelegd in een test
Het commentaar in het manifest noemt het “een belofte aan gezinnen”.
Het samengevoegde manifest van Android mag om internet en betalen via de winkel vragen; de toestemmingen voor de advertentie-ID en voor opslag zijn verwijderd.
Bij elke run gecontroleerd door een test
Regel 2.3.1 van de App Store.
De pagina kan het niet bereiken met een statische import, en geen van de bestanden ervan mag een aankoop toekennen, melden of wegschrijven.
Bij elke run gecontroleerd door een test
volgende update Zout water dat door een batterij wordt gesplitst, bubbelt en wordt zuur en zeepachtig, maar de simulatie maakt geen zuurstof of chloor.
“Geen woord over de elektrolyse, in de code of de catalogus, zegt zuurstof of chloor.”
De simulatie maakt geen beeld. Ze houdt een stapel getallen bij voor elk plekje op het papier, elk ongeveer een halve millimeter breed: hoeveel kleur er is ingetrokken, hoe nat het is, hoe dik de ketchup ligt, hoe heet en hoe verkoold. Elk beeld zet een klein programma op de grafische chip die getallen om in een belicht oppervlak voor elke pixel op het scherm.
Kleur wordt bewaard zoals licht erdoorheen gaat. Elk plekje slaat op hoe sterk het rood, groen en blauw opneemt, en het papier schijnt erdoorheen als e tot de macht min dat. Twee lagen waterverf over elkaar tellen hun absorbantie op, dus geel over blauw geeft groen, zoals bij glacislagen. Dat is de wet van Beer-Lambert Swinehart 1962, waarvan we na het bouwen ontdekten dat hij ons model beschreef, in drie kanalen. Echte verf verstrooit ook licht Kubelka 1931; dat laat het spel weg.
Het papier heeft echt reliëf. Een deuk van een hard aangedrukt potlood, de verhoogde omtrek van een kleurplaat, de golven waarin nat papier opdroogt, een klodder ketchup en een plas erbovenop tellen allemaal op tot één hoogte. Het licht leest die hoogte om hellingen te schaduwen, korte slagschaduwen te werpen en holtes donkerder te maken. Het water leest dezelfde deuken en omtrekken, dus volgens de code loopt het een groef in en blijft het binnen een verhoogde lijn. Een potloodgroef is wel ondiep, en in het spel zie je die niet, en ook geen water dat erin loopt. Een klodder ketchup of dikke verf laat zijn reliëf duidelijk zien.
Glans volgt ook de hoogte. Een glanzend oppervlak kaatst de lamp alleen terug waar het ernaartoe kantelt, dus een grote, vlakke plas heeft midden in bijna geen glanslicht, en zijn randen en rimpelingen glanzen. Eén keer gieten trekt binnen een seconde of twee in, voordat het dat kan laten zien. Nat papier wordt donkerder, en alleen papier dat helemaal doorweekt is, begint te glanzen.
De korrel van het papier is ruis die één keer per vel wordt gemaakt: een fijne ruwheid plus langere vezels. Nat papier golft op terwijl het doorweekt en wordt bij het drogen nooit helemaal vlak, omdat zijn golven stoppen met krimpen bij een lage bodem in plaats van plat te worden. Een strijkijzer of een deegroller drukt ze plat. Brand een gat en de geschilderde houten tafel komt erdoorheen.
Een hoekje van de pagina, 120 × 80 cellen, belicht met de eigen formules van het spel: dezelfde hoogtesom, hetzelfde hoofdlicht, dezelfde schaduwmars, occlusie en glans. Er is net water gegoten op een ingedrukte groef, met ketchup en honing in de buurt en de verhoogde omtrek van een kleurplaat. Kies iets en sleep over het papier, of druk op Voeg toe, of giet opnieuw. Waar je op let: het water vlakt uit, vult de groef en trekt binnen een paar seconden in; het natte papier blijft donkerder en golft op terwijl het droogt. Het midden van de honing blijft dof terwijl de rand glanst. Zet schaduwen of glans uit om te zien wat elk toevoegt, en minder schaduwstappen om te zien wat een trage tablet opgeeft. In het spel is een potloodgroef te ondiep om te zien, en we hebben nooit gegoten water er een in zien lopen.

Elk glanslicht piekt bij een kanteling van 22,5° naar het hoofdlicht: 0,85 voor water of honing, 0,77 voor ketchup, 0,51 voor mosterd en 0,07 voor droog papier.
| kanteling naar het hoofdlicht (graden) | droog papier | mosterd | ketchup | water of honing |
|---|---|---|---|---|
| 0 | 0,01347 | 0,00047 | 0,00003 | 0,00001 |
| 0,5 | 0,01449 | 0,00065 | 0,00005 | 0,00002 |
| 1 | 0,01556 | 0,00088 | 0,00008 | 0,00003 |
| 1,5 | 0,01668 | 0,00119 | 0,00012 | 0,00006 |
| 2 | 0,01784 | 0,00159 | 0,00018 | 0,00009 |
| 2,5 | 0,01906 | 0,00211 | 0,00028 | 0,00014 |
| 3 | 0,02032 | 0,00278 | 0,00041 | 0,00021 |
| 3,5 | 0,02162 | 0,00364 | 0,00061 | 0,00033 |
| 4 | 0,02297 | 0,00472 | 0,00089 | 0,00049 |
| 4,5 | 0,02436 | 0,00609 | 0,00128 | 0,00074 |
| 5 | 0,02579 | 0,00779 | 0,00183 | 0,0011 |
| 5,5 | 0,02726 | 0,00989 | 0,00258 | 0,0016 |
| 6 | 0,02876 | 0,01246 | 0,00361 | 0,00232 |
| 6,5 | 0,0303 | 0,01559 | 0,00499 | 0,00331 |
| 7 | 0,03186 | 0,01937 | 0,00682 | 0,00467 |
| 7,5 | 0,03345 | 0,02389 | 0,00923 | 0,00652 |
| 8 | 0,03505 | 0,02925 | 0,01236 | 0,009 |
| 8,5 | 0,03668 | 0,03556 | 0,01639 | 0,01229 |
| 9 | 0,03831 | 0,04293 | 0,02151 | 0,01658 |
| 9,5 | 0,03996 | 0,05146 | 0,02793 | 0,02212 |
| 10 | 0,0416 | 0,06125 | 0,03591 | 0,02917 |
| 10,5 | 0,04325 | 0,07238 | 0,0457 | 0,03806 |
| 11 | 0,04488 | 0,08495 | 0,05758 | 0,0491 |
| 11,5 | 0,0465 | 0,09901 | 0,07182 | 0,06264 |
| 12 | 0,0481 | 0,11458 | 0,08868 | 0,07903 |
| 12,5 | 0,04968 | 0,1317 | 0,10842 | 0,09863 |
| 13 | 0,05123 | 0,15033 | 0,13122 | 0,12173 |
| 13,5 | 0,05274 | 0,17041 | 0,15726 | 0,14861 |
| 14 | 0,05421 | 0,19186 | 0,1866 | 0,17945 |
| 14,5 | 0,05563 | 0,21453 | 0,21924 | 0,21434 |
| 15 | 0,057 | 0,23825 | 0,25505 | 0,25325 |
| 15,5 | 0,05831 | 0,26279 | 0,29381 | 0,296 |
| 16 | 0,05956 | 0,28789 | 0,33516 | 0,34223 |
| 16,5 | 0,06073 | 0,31325 | 0,3786 | 0,39144 |
| 17 | 0,06184 | 0,33855 | 0,4235 | 0,44292 |
| 17,5 | 0,06286 | 0,36342 | 0,46913 | 0,4958 |
| 18 | 0,0638 | 0,3875 | 0,51463 | 0,54907 |
| 18,5 | 0,06466 | 0,4104 | 0,55908 | 0,60157 |
| 19 | 0,06542 | 0,43172 | 0,60149 | 0,65207 |
| 19,5 | 0,06609 | 0,45112 | 0,64087 | 0,69929 |
| 20 | 0,06667 | 0,46822 | 0,67623 | 0,74194 |
| 20,5 | 0,06714 | 0,48273 | 0,70667 | 0,77884 |
| 21 | 0,06751 | 0,49435 | 0,73136 | 0,80889 |
| 21,5 | 0,06778 | 0,50287 | 0,74962 | 0,83118 |
| 22 | 0,06794 | 0,50812 | 0,76094 | 0,84502 |
| 22,5 | 0,068 | 0,51 | 0,76499 | 0,84999 |
| 23 | 0,06795 | 0,50846 | 0,76166 | 0,84592 |
| 23,5 | 0,0678 | 0,50354 | 0,75105 | 0,83293 |
| 24 | 0,06754 | 0,49534 | 0,73346 | 0,81145 |
| 24,5 | 0,06718 | 0,48401 | 0,70938 | 0,78213 |
| 25 | 0,06672 | 0,46978 | 0,67948 | 0,74587 |
| 25,5 | 0,06615 | 0,45292 | 0,64456 | 0,70373 |
| 26 | 0,06549 | 0,43374 | 0,60554 | 0,65691 |
| 26,5 | 0,06474 | 0,41258 | 0,56338 | 0,60668 |
| 27 | 0,06389 | 0,38983 | 0,51909 | 0,55432 |
| 27,5 | 0,06296 | 0,36585 | 0,47365 | 0,50107 |
| 28 | 0,06194 | 0,34104 | 0,42799 | 0,4481 |
| 28,5 | 0,06085 | 0,31576 | 0,38298 | 0,39644 |
| 29 | 0,05968 | 0,29039 | 0,33937 | 0,34698 |
| 29,5 | 0,05844 | 0,26525 | 0,2978 | 0,30042 |
| 30 | 0,05713 | 0,24064 | 0,25876 | 0,25732 |
| 30,5 | 0,05577 | 0,21684 | 0,22264 | 0,21802 |
| 31 | 0,05435 | 0,19405 | 0,18968 | 0,18272 |
| 31,5 | 0,05289 | 0,17248 | 0,16001 | 0,15148 |
| 32 | 0,05138 | 0,15225 | 0,13365 | 0,12422 |
| 32,5 | 0,04984 | 0,13348 | 0,11053 | 0,10075 |
| 33 | 0,04826 | 0,11621 | 0,0905 | 0,08082 |
| 33,5 | 0,04666 | 0,10048 | 0,07337 | 0,06413 |
| 34 | 0,04504 | 0,08627 | 0,05888 | 0,05032 |
| 34,5 | 0,04341 | 0,07356 | 0,04678 | 0,03905 |
| 35 | 0,04177 | 0,06229 | 0,03679 | 0,02997 |
| 35,5 | 0,04012 | 0,05237 | 0,02865 | 0,02274 |
| 36 | 0,03848 | 0,04372 | 0,02208 | 0,01707 |
| 36,5 | 0,03684 | 0,03624 | 0,01684 | 0,01266 |
| 37 | 0,03521 | 0,02983 | 0,01272 | 0,00929 |
| 37,5 | 0,0336 | 0,02438 | 0,0095 | 0,00674 |
| 38 | 0,03201 | 0,01978 | 0,00703 | 0,00483 |
| 38,5 | 0,03045 | 0,01594 | 0,00514 | 0,00343 |
| 39 | 0,02891 | 0,01274 | 0,00373 | 0,0024 |
| 39,5 | 0,02741 | 0,01012 | 0,00267 | 0,00166 |
| 40 | 0,02593 | 0,00797 | 0,00189 | 0,00114 |
| 40,5 | 0,0245 | 0,00624 | 0,00133 | 0,00077 |
| 41 | 0,0231 | 0,00484 | 0,00092 | 0,00052 |
| 41,5 | 0,02175 | 0,00373 | 0,00063 | 0,00034 |
| 42 | 0,02044 | 0,00286 | 0,00043 | 0,00022 |
| 42,5 | 0,01918 | 0,00217 | 0,00029 | 0,00014 |
| 43 | 0,01796 | 0,00163 | 0,00019 | 0,00009 |
| 43,5 | 0,01679 | 0,00122 | 0,00013 | 0,00006 |
| 44 | 0,01567 | 0,00091 | 0,00008 | 0,00004 |
| 44,5 | 0,0146 | 0,00067 | 0,00005 | 0,00002 |
| 45 | 0,01357 | 0,00049 | 0,00003 | 0,00001 |
Bron: Berekend met de eigen glansregel van het spel, een Blinn-Phong-glanslicht dat scherper wordt met de glans, halverwege tussen het hoofdlicht en het oog.
Plaatje opslaan blaast het scherm niet op. De worker pakt de hele pagina opnieuw in, en hetzelfde grafische programma draait nog eens met een afdrukschakelaar aan, 3200 pixels aan de lange kant, of 2400 op een telefoon of een apparaat met weinig geheugen. Bij de 512 cellen breed van de volle tafel is dat 6,25 pixel per cel.
Tussen cellen bemonstert het langs krommen in plaats van rechte hellingen. Waar de cellen eromheen een echte trede hebben, zoals een stiftlijn of de rand van een vlek, drukt het de zachte helling weer samen tot een scherpe rand, en zachte lagen waterverf laat het met rust. Het leunt een beetje naar de inkt, zodat dunne lijnen heel blijven, en de vezels van het papier maken elke rand wat rafelig. Pigment zakt in de kuiltjes van het papier. Was van waskrijt pakt alleen de toppen van de ruwheid, zodat het papier er in spikkels doorheen komt. Gaten, verkoling en de randen van smurrie worden een pixel breed uitgesneden.
Daarna worden de dingen die op het papier liggen eroverheen getekend vanuit hun schilderingen, zonder wat je in je hand hebt en zonder de hulplijnen. Een grote tekening wordt in banden van rijen gemaakt, elk met een pauze teruggelezen, zodat het spel tijdens het opslaan blijft reageren.
Dit detail is opnieuw opgebouwd, niet gesimuleerd. Het rooster eronder is hetzelfde, en de fijne vezel in een afdruk wordt alleen uit de positie uitgerekend, zodat elke afdruk dezelfde korrel heeft.
Een kleine pagina blauwe inkt: een vlek met een harde rand, een zachte laag, een stiftlijn en potloodarcering. Op beide manieren getoond wordt hetzelfde stukje twee keer getekend: links zoals op het scherm, rechts volgens de regels van het bewaarde plaatje. Trek stiftlijnen met je vinger, of druk op Trek een stiftlijn. Waar je op let: de rand van de vlek gaat van een helling van ongeveer tien pixels breed naar ongeveer drie, terwijl de zachte laag zijn verloop houdt. Er verschijnt korrel in het papier en in het pigment. Zoom in om te zien dat de randen van de afdruk scherp blijven. (Vlak belicht, zonder schaduwen, om het snel te houden.)


Alles wat als voorwerp op het papier ligt, is een schildering: een beker, een tandenstoker, een fles, de poes. Dingen op tafel zijn recht van boven geschilderd, zoals een kaart ze laat zien, in één gouachestijl met warm licht van linksboven, dezelfde kant als de lamp in het grafische programma. Gereedschap in je hand is van opzij geschilderd, want zo zie je het als je het vasthoudt.
Elke schildering begint als een gegenereerd beeld, gemaakt met een beeldmodel in één gedeelde stijl, met de eigen kunst van het spel als leidraad en het plankpictogram van het gereedschap ernaast, zodat het ding in je hand het ding op de plank is. Elk ervan wordt op het oog uitgekozen. Scripts maken het daarna schoon, snijden het in spritesheets en meten dingen zoals waar een tuitje naartoe wijst uit de verf zelf. Delen die een kleur aannemen, worden een eigen laag, zodat één schildering van een waskrijtje voor elke kleur waskrijt dient.
Een ding wisselt van schildering als het van toestand wisselt. In het uitgebrachte spel betekent dat ingeknepen flessen, een tandenstoker die rechtop staat, gebroken en in stukken, klemmen open en dicht, en de houdingen van elk dier. De volgende update voegt er meer toe, zoals flessen die op hun zij liggen, en ballen waarvan de vlakken komen van de echte vorm van een voetbal volgende update.
Twee regels houden het zo. Iets waarvan de schildering nog niet binnen is, tekent niets tot die er is, en een gereedschap waarvan de kunst niet af is, blijft van de plank. En een test leest alle tekencode aan de schermkant: elke functie die vormen of tekst tekent, moet op een lijst staan, met de reden waarom het geen voorwerp is. De volgende update voegt de functies toe die pixels schrijven volgende update. Vloeistoffen, vuur, schaduwen, licht, kabels, hulplijnen en de tekentjes op de knoppen blijven met opzet code.
Dieren zijn schilderingen die buigen. Het lijf van een poes is één schildering van bovenaf, gespannen over een net dat haar botten volgt, zodat ze draait en zich uitrekt zonder naad. Haar poten en staart zijn eigen geschilderde stukken, en een nieuwe houding vloeit in vanuit een andere schildering.

Professor Oops heeft dieren: vliegen, mieren, een duif, een poes, een hond en slakken. De regel is dat geen van hen ooit gewond raakt. Ze ontwijken, vluchten of trekken zich terug. Een test zet elk ervan door een lange lijst onheil, van een hamer en een deegroller tot een vuurtje ernaast, de oven en lang wachten. Hij controleert dat er niets op het dier terechtkomt, er geen vlam onder wordt aangestoken, het niet verdwijnt en dat het bewoog toen er iets op afkwam.
Bij de release bevatte de lijst 61 soorten onheil voor 8 toestanden van dieren, 488 runs in totaal; ongeveer 25 van die soorten gebruiken gereedschap dat nog wordt afgemaakt. De volgende update heeft 97 soorten voor 10 toestanden, 970 runs volgende update.
Veiligheid komt van een wereld die meegeeft, niet van snelle dieren. Elke druk, welk gereedschap het ook is, gaat door één gedeelde regel die leest wat waar drukt. Een klein dier wordt door de druk zelf opzij geschoven. In de volgende update wacht een zwaar ding boven een poes of een hond in de lucht tot het dier is weggegaan, en wordt het weggehaald als het dier niet beweegt volgende update.
Het spel bevat geen reclamecode en geen analysebibliotheek van een ander bedrijf. In één onderzoek naar 135 apps die jonge kinderen gebruiken, had 95% reclame Meyer 2019. Op Android is de toestemming voor de advertentie-ID, die een bibliotheek zou kunnen binnensmokkelen, verwijderd, samen met de toestemmingen voor opslag, en een test bewaakt de lijst. Op iOS meldt de app geen tracking. Het uitgebrachte spel gebruikt nooit de microfoon of de camera. Anonieme tellingen, zonder kenmerk en zonder tijd fijner dan een dag, kan een volwassene uitzetten, en ze staan bij het begin uit in het Verenigd Koninkrijk en het grootste deel van Europa.
Het hele spel zit in de app, de WebAssembly-kernels ook, ingepakt in de code zodat er niets wordt opgehaald. Bij de release zit de stem van de schilder ook in de app. In de volgende update worden stem en de meeste muziek pakketten die worden opgehaald als er verbinding is, en alles werkt ook zonder volgende update. De browserversie heeft een verbinding nodig om te laden.
Op de eerste dag, toen er alleen een prototype draaide, werden een paar honderd ideeën opgeschreven en gescoord op plezier, gemak, hoe nieuw ze waren en hoeveel “dat mag je nooit doen”-sensatie ze gaven. Drie van de acht hoogst scorende nieuwe ideeën waren een voetstap van een reus, een niesbui van een reus en een zwart gat. Dezelfde dag ging een harde regel boven de scores: alleen echte dingen die een kind echt met papier kan doen. Geen monsters, geen reuzen, geen zwarte gaten. Geen test kan dat controleren. Het wordt vastgehouden door wie beslist wat er wordt gebouwd. Sommige zachte ideeën die laag scoorden, kwamen er toch: rode kool die van kleur verandert en een laag van theevlekken als gereedschap, en een zonneafdruk en een potloodwrijfsel zonder eigen gereedschap, omdat de gedeelde natuurkunde ze maakte.
Kopen, het deel voor volwassenen en extra speeltijd zitten achter een som zoals 37 + 40. Die is bedoeld om een jong kind tegen te houden, en de code zegt dat ook: het is een som, geen slot. Aanbiedingen zijn zeldzaam: hooguit drie vragen, een week uit elkaar, nooit meer na “Nee, bedankt, vraag het niet meer”. Een ouder kan een dagelijkse grens van 15, 30, 45 of 60 minuten instellen, standaard uit, en de schilder zegt het twee minuten voor het einde.
een test
Elke toestand van elk dier krijgt elke opgesomde soort onheil: 488 runs bij de release, 970 in de volgende update volgende update.
Bij elke run gecontroleerd door een test
een test
Geen reclamecode en geen analysebibliotheek van een ander bedrijf. De toestemmingen voor de advertentie-ID en voor opslag zijn uit de Android-build gehaald.
Vastgelegd in een test
hoe het gebouwd is
Het hele spel, de WebAssembly-kernels ook, zit ingepakt in de app, dus er wordt niets opgehaald om te spelen.
De kernels reizen mee in de code van het spel
code
Kopen, het deel voor volwassenen en extra tijd zitten achter een som zoals 37 + 40. Die is bedoeld om een jong kind tegen te houden, niet om een slot te zijn.
In de code vastgelegd, en daar een som genoemd
mensen
Geen monsters, geen reuzen, geen zwarte gaten. Geen test kan dat controleren; het wordt vastgehouden door wie beslist wat er wordt gebouwd.
een besluit genomen op de eerste dag
Elk van deze loopt van iets wat een kind doet, door een laag die veel systemen delen, naar wat er volgt. Een test legt elk ervan vast.
Gedeeld: een vingerafdruk van de gekopieerde broncode
De testrun mislukt, met de naam van de functie erbij, tot de WebAssembly-kopie is overgezet. Vastgelegd in een test.
Gedeeld: deuk in de grondhoogte
Het grijs slaat de ingedrukte lijn over, die schoon blijft, met donkere randen waar de wanden van de groef het grijs opvangen. Vastgelegd in een test.
Gedeeld: één druk, gelezen door de gedeelde gevarenregel
De mier wordt opzij geschoven en blijft er nooit onder liggen. Vastgelegd in een test.
Gedeeld: twee lagen samengevouwen tot één texel door de shader achterstevoren op te lossen
De ketchup wordt nauwelijks bleker, en aan het front van de olie is geen harde lijn te zien. Vastgelegd in een test.
Gedeeld: een verse volledige verpakking, dezelfde shader met afdrukken aan
Het gat en de tafel eronder staan ook in het bewaarde plaatje; een controle in de browser zorgt dat er een echte JPEG van 3200 pixels uitkomt, in de workermodus net als in dezelfde thread.