---
title: "Waarom mijn foto-tool wel op mijn laptop werkte en niet op de iPhone"
date: 2026-07-29
author: ""
featured_image: "https://1704media.nl/wp-content/uploads/2026/07/pbn-canvas.jpg"
categories:
  - name: "Uncategorized"
    url: "/category/uncategorized.md"
---

# Waarom mijn foto-tool wel op mijn laptop werkte en niet op de iPhone

Een bezoeker uploadt een vakantiefoto in mijn tool die een foto omzet naar schilderen op nummer. Op mijn laptop rolt er netjes een patroon uit met genummerde vlakken. Op zijn iPhone: niks. De pagina hangt even en gooit dan *Maximum call stack size exceeded*. Bij de ene foto wel, bij de andere niet. Precies het soort bug waar je gek van wordt, want op mijn eigen scherm werkte alles.

Deze week heb ik hem gevonden en gefixt. Het is een mooi voorbeeld van iets dat elke beginnende bouwer een keer overkomt, dus ik leg het stap voor stap uit.

## Wat de tool eigenlijk doet

De tool op paintingbynumberstool.com zet een foto om in een schilderij op nummer. Hij groepeert alle pixels met bijna dezelfde kleur tot één vlak, geeft dat vlak een nummer en een kleurcode, en tekent de omtrek. Zo’n vlak vinden gaat met een techniek die **flood-fill** heet: je begint bij één pixel en loopt naar alle buurpixels die dezelfde kleur hebben, en van daaruit weer naar hún buren, tot je de rand van het vlak raakt. Precies wat de emmer-knop in Paint doet.

De eerste versie deed dat recursief. In gewone taal: de functie riep voor elke buurpixel zichzelf opnieuw aan. Kort om op te schrijven, en op een egale foto werkt het prima.

## Waarom het op mijn laptop wél werkte

Elke keer dat een functie zichzelf aanroept, legt de browser dat op een stapel — de call-stack. Bij een egaal vlak van tweehonderd pixels is die stapel tweehonderd hoog, geen probleem. Maar geef de tool een foto met bestrating of gebladerte: grillige vlakken van duizenden aaneengesloten pixels. Dan wordt die stapel duizenden lagen diep, en op een gegeven moment is hij vol.

En hier zit de valstrik. De browser op mijn laptop draait op V8, de motor van Chrome. Die heeft een royale call-stack. De browser op een iPhone draait op JavaScriptCore, de motor van Safari, en die stapel is een stuk kleiner. Dezelfde foto, dezelfde code, maar op de telefoon liep de stapel vol waar hij op mijn scherm nog net paste. Daarom leek het willekeurig: het hing niet aan de foto, het hing aan het apparaat waarop je hem opende.

Dit is de les die ik het vaakst opnieuw moet leren: “werkt bij mij” zegt niks. Je bouwt op een dikke laptop met de ene browser, je bezoeker zit op een telefoon met de andere. Testen op het echte apparaat is geen extraatje, het is de test.

## De fix: de stapel zelf beheren

De oplossing is bekend, maar je moet het even zien om het te snappen. In plaats van de functie zichzelf te laten aanroepen, maak je zelf een lijstje van pixels die je nog moet bekijken. Je pakt er één van af, kijkt naar de buren, gooit de goede buren op het lijstje, en gaat door tot het lijstje leeg is. Zelfde route door de foto, alleen ligt de administratie nu in het gewone geheugen in plaats van op die krappe call-stack. Dat geheugen is bij een browser vele malen groter, dus een vlak van tienduizend pixels is geen punt meer.

De rest van het algoritme heb ik met rust gelaten. Alleen die drie zelf-aanroepen zijn een lijstje geworden. Klein, maar het verschil tussen crashen en werken.

## Bewijzen dat het écht hetzelfde doet

Een patroonmotor herschrijven is eng, want als de nieuwe versie nét iets andere vlakken tekent, ziet je hele tool er anders uit en weet je niet waarom. Dus ik heb het niet op mijn blauwe ogen geloofd. Ik heb 307 willekeurige patronen door de oude en de nieuwe versie gehaald en de uitkomsten vergeleken: identiek. Een leeg vlak van 1100 bij 880 pixels: identiek. En in de volledige tool telde Chrome zowel voor als na de fix precies 990 vlakken. Zelfde getal, dus zelfde resultaat.

Daarna de echte test, op Safari zelf. Vóór de fix: crash. Erna: 984 vlakken, netjes een patroon. Klaar.

## Twee dingen die ik meeneem

Eén: recursie is elegant om op te schrijven, maar in de browser is je call-stack klein en verschilt hij per apparaat. Bij iets dat door duizenden pixels of nodes kan lopen — een foto, een boom, een graaf — beheer je die stapel liever zelf. Dan hangt je tool niet af van hoe royaal de telefoon van je bezoeker is.

Twee: als je een rekenhart vervangt, herschrijf je het niet zomaar en hoop je er het beste van. Je laat de oude en de nieuwe versie op honderden gevallen exact hetzelfde antwoord geven, en pas dan durf je het live te zetten. Dat kostte me een half uur extra en het bespaarde me een week zoeken naar een verschil dat niemand had aangekondigd.

Nog niet alles is af. Op Safari doet de motor er bij een grote foto zo’n twee minuten over, dat is te traag en dat pak ik nog aan. Maar de crash is weg, en dat was het punt: een tool die op de helft van de telefoons omvalt, is geen tool.