Der Anfang sieht folgendermaßen aus:
/*
* Demo-Skript zur Berechnung der Mandelbrotmenge
* - Stand 29.09.2026 -
* von speerchen, www.speerchen.de
*/
function demo() {
Der function()-Befehl wurde eingefügt, damit das Skript das Drücken der Schaltfläche abwartet und nicht sofort startet, sobald die Seite vom Browser aufgerufen wird.
Danach erfolgt die Javascript-spezifische Initialisierung des Grafikbildschirmes, die hier nicht weiter erläutert werden soll:
bildElement = document.getElementById('fraktal');
if (bildElement.getContext('2d')) {
bild = bildElement.getContext('2d');
} else {
alert("Die erforderliche Grafikfunktion ist im Browser leider nicht vorhanden.");
}
Als nächstes lege ich – gleich in der Variablendeklaration – die Höhe und Breite der Grafik fest:
/*
* Breite des Bildes in Pixel
*/
var breite = 320;
/*
* Hoehe des Bildes in Pixel
*/
var hoehe = 240;
Der «mathematische Teil» fängt mit der Überlegung an, dass die Bildschirmoberfläche nichts anderes als einen Ausschnitt aus einem mathematischen Koordinatensystem darstellt. Die Position jedes Pixels steht für ein Zahlenpaar aus «x» und «y». Und jedem dieser Zahlenpaare wird durch die Berechnung eine «natürliche» (d.h. positive ganze) Zahl und dieser wiederum eine Farbe zugeordnet.
Um dies zu programmieren, muss man sich deshalb erstmal Gedanken darüber machen, welcher Ausschnitt des Koordinatensystems dargestellt werden soll, also welcher Wert für x «ganz links» steht und welcher Wert für y «ganz unten». Das Gleiche auch für «ganz rechts» und «ganz oben.»
Folgende Werte haben sich bewährt:
/*
* Linke Kante der Grundfigur
*/
var intervallLinks = -3.0;
/*
* Untere Kante der Grundfigur
*/
var intervallUnten = -1.875;
/*
* Rechte Kante der Grundfigur
*/
var intervallRechts = 2.0;
/*
* Obere Kante der Grundfigur
*/
var intervallOben = 1.875;
Und jetzt noch die Deklaration von ein paar anderen Variablen:
/*
* Maximale Rekursionstiefe
*/
var grenzeRekursion = 50;
/*
* Betragsgrenze Rekursion
*/
var grenzeBetrag = 4.0;
/*
* Quadrat der Betragsgrenze Rekursion
*/
var grenzeBetragQuadrat = grenzeBetrag * grenzeBetrag;
/*
* Farbtabelle
*/
var farben = new Array("rgb(0,0,0)",
"rgb(0,0,64)", "rgb(0,0,85)", "rgb(0,0,106)", "rgb(0,0,127)", "rgb(0,0,148)",
"rgb(0,0,170)", "rgb(0,0,191)", "rgb(0,0,212)", "rgb(0,0,233)", "rgb(0,0,255)",
"rgb(6,6,249)", "rgb(12,12,243)", "rgb(19,19,236)", "rgb(25,25,230)", "rgb(31,31,224)",
"rgb(38,38,218)", "rgb(44,44,211)", "rgb(51,51,204)", "rgb(57,57,198)", "rgb(63,63,192)",
"rgb(70,70,185)", "rgb(76,76,179)", "rgb(82,82,173)", "rgb(89,89,166)", "rgb(95,95,160)",
"rgb(102,102,153)", "rgb(108,108,147)", "rgb(114,114,141)", "rgb(121,121,134)", "rgb(127,127,128)",
"rgb(133,133,122)", "rgb(140,140,115)", "rgb(146,146,109)", "rgb(153,153,102)", "rgb(159,159,96)",
"rgb(165,165,90)", "rgb(172,172,83)", "rgb(178,178,77)", "rgb(184,184,71)", "rgb(191,191,64)",
"rgb(197,197,58)", "rgb(204,204,51)", "rgb(210,210,45)", "rgb(216,216,39)", "rgb(223,223,32)",
"rgb(229,229,26)", "rgb(235,235,20)", "rgb(242,242,13)", "rgb(248,248,7)", "rgb(0,0,0)");
Wozu all diese Variablen nun alle nütze sind, werden wir gleich noch sehen. An dieser Stelle nur zwei Dinge:
Die Variable grenzeRekursion ist eine willkürliche Festlegung, wie viele verschiedene natürliche Zahlen bei der Berechnung herauskommen können. Jedem Zahlenwert muss eine Zahl zugeordnet werden.
Das Array farben[] im Beispiel beinhaltet insgesamt 51 (von 0 bis 50 durchnummerierte) Farben. Was in diesen Arrays vorgegeben wird, ist im Prinzip Geschmackssache. (Ich persönlich empfehle Farbverläufe und mag Blau am liebsten :-)
Jetzt aber zum eigentlichen Algorithmus.
/*
* Bildaufbau
*/
Am Anfang stehen erstmal zwei ineinandergeschachtelte for-Schleifen, die nichts anderes zu tun haben, als nacheinander jedes Pixel auf dem Programmarbeitsbereich abzuklappern. Gleichzeitig mit der Bestimmung der pixelmäßigen Position auf dem Bildschirm wird auch gleich mit berechnet, welcher Punkt auf dem «wirklichen» Koordinatensystem (mit seinen komplexen Zahlen, also Zahlenpaaren aus reellen Zahlen) dazugehört. Dieser wird in den Variablen x und y abgelegt. Das alles sieht wie folgt aus:
for (var pixelY = 0; pixelY < hoehe; pixelY++) {
var y = intervallUnten + (intervallOben - intervallUnten) * pixelY / (hoehe - 1);
for (var pixelX = 0; pixelX < breite; pixelX++) {
var x = intervallLinks + (intervallRechts - intervallLinks) * pixelX / (breite - 1);
Und erst hier fängt die Berechnung eines der einzelnen Punkte an. Dies wird wiederum in einer eigenen Schleife erfolgen. Zur Vorbereitung dieser Schleife werden diverse Variablen, die später gebraucht werden, auf den Anfangswert von 0 gesetzt.
var zaehler = 0;
var gliedX = 0;
var gliedY = 0;
var gliedXQuadrat = 0;
var gliedYQuadrat = 0;
Als nächstes wird die vorbereitete Schleife gestartet. Und deren Inhalt macht dann die eigentliche Arbeit zur Berechnung eines Punktes!
do {
Die Variable zaehler hat dabei entscheidende Funktionen: Nicht nur, dass sie die Anzahl der Schleifendurchläufe mitzählt - und für den Abbruch sorgt, wenn die in der Variablen rekursionsgrenze festgelegte Obergrenze erreicht wird: Der Inhalt dieser Variablen ist nach erledigtem Schleifendurchlauf gleichzeitig diejenige «natürliche Zahl», aus der sich die Farbe des entsprechenden Pixels ergibt!
Die Variablen gliedX und gliedY werden bei jedem Schleifendurchlauf neu berechnet, und zwar basierend auf ihrem Wert beim jeweils vorherigen Schleifendurchlauf. Diese sind (beide zusammen) die eigentliche komplexe Zahl, die bei jedem Rechendurchlauf neu berechnet und weiterverwendet werden.
Die Variablen gliedX2 und gliedY2 dienen eigentlich nur dazu, den Programmablauf «performanter» zu gestalten. In ihnen befindet sich jeweils die Quadrate von gliedX und gliedY. Diese werden im Programmablauf an zwei verschiedenen Stellen gebraucht.
Die Variable hilfX ist eine Hilfsvariable, die zum Berechnen benötigt wird.
Und jetzt wird's interessant: Die Zahlenpaare « gliedX / gliedY » und auch « x / y » werden hier als komplexe Zahlen angesehen. Eine komplexe Zahl ist ein Zahlenpaar, das sich zusammensetzt aus x + yi, wobei gilt: i2 = -1. Es gibt folgende Rechenregeln, die wir hier brauchen:
Die Schleife fängt an (siehe oben) mit der komplexen Zahl "0 + 0i" (deren Quadrat lautet genauso). Diese Beträge sind in gliedX und gliedY gespeichert. Diese Zahl wird nun quadriert, und anschließend wird die komplexe Zahl «x + yi» hinzuaddiert. (Dies ganze ist übrigens eine "rekursive Folge»; die einzelnen Zahlen, die so nacheinander herauskommen, nennt man «Folgeglieder». Man kann auch zur Berechnung der Folgeglieder schreiben: cn = (cn-1)2 + d).
Aus diesem Berechnungvorgang und den beschriebenen Formeln ergibt sich folgende Befehlsfolge, die mit jedem Schleifendurchlauf wiederholt wird:
var hilfX = gliedX * gliedY;
gliedX = gliedXQuadrat - gliedYQuadrat + x;
gliedY = hilfX + hilfX + y;
gliedXQuadrat = gliedX * gliedX;
gliedYQuadrat = gliedY * gliedY;
zaehler++;
An dieser Stelle endet der Inhalt der Schleife. Hier wird geprüft, ob ein weiteres Folgeglied zu berechnen ist. Schluss ist dann, wenn entweder die vorgegebene maximale Zahl von Durchläufen erreicht ist - oder schon eher, wenn der »Betrag« der komplexen Zahl eine bestimmte – ebenfalls am Programmanfang vorgegebene – Größe überschreitet. Diese Größe ist in der Variable Betragsgrenze gespeichert.
} while (zaehler < grenzeRekursion
&& gliedXQuadrat + gliedYQuadrat < grenzeBetragQuadrat);
Hier ein kleines Perfomance-Tuning: Es wird hier nicht (wie es sich eigentlich aus der Berechungsformel ergäbe) der Betrag mit der Betragsgrenze verglichen, sondern das Quadrat des Betrages mit dem Quadrat der Betragsgrenze (betragsgrenze2) – dies ergibt das gleiche Ergebnis und spart das «zeitraubende» Wurzelziehen.
Die eigentliche Zahl, die bei der Berechnung herauskommt und farblich dargestellt werden soll, ist dabei die Anzahl der Schleifendurchläufe. Diese ist gespeichert in der Variable zaehler.
Jetzt muss aus der Variable zaehler eine Farbe bestimmt und der Punkt mit dieser Farbe gezeichnet werden. Das wird in diesem Skript durch Entnahme aus dem o.g. Array von Farben bewerkstelligt und sieht wie folgt aus.
(Übrigens: Der Passus "hoehe - " vor dem Y-Wert ist erforderlich, weil die Y - Achse auf dem normalen Koordinatensystem und dem Grafikbildschirm unterschiedlich ausgerichtet sind; ohne dieser Korrektur würde das Bild auf dem Kopf stehen.)
bild.fillStyle = farben[zaehler];
bild.fillRect(pixelX, hoehe - pixelY,1,1);
Und nun noch das Ende der beiden for-Schleifen kennzeichnen:
}
}
(In BASIC würde hier so etwas wie "next pixelX" ... "next pixelY" stehen....)
Und - "Schluss" natürlich nicht vergessen, sonst gibt es Fehlermeldungen:
}
Im Vergleich zum Skript, das im Hintergrund der verschiedenen Elemente der Galerie seinen Dienst verrichtet, handelt sich bei diesem Programm nur um eine «Magerversion».
Es geht mir hier nur darum, das Funktionsprinzip zu verdeutlichen, und ich habe deshalb alles vereinfacht oder weg gelassen, was nicht unbedingt vom Berechnungsalgorithmus mit einer einfachen Darstellung benötigt wird.
Aber - das grundsätzliche Prinzip ist gleich.