P5 in a Nutshell - Die wichtigsten Funktionen auf einen Blick

Das Canvas-Prinzip

Grundlegend für die Arbeit mit P5 ist das mit HTML5 eingeführte "Canvas-Prinzip", in dessen Rahmen ein Bereich auf der Webseite quasi als Leinwand gilt, auf der beliebig programmierbare Aktionen möglich sind.

Der Canvas wird in der setup()-Funktion bestimmt. Im Grunde reichen die Zeilen

<script>
function setup(){

createCanvas(800,400); // Canvas mit der Weite von 800 Pixeln
// und der Höhe von 400 Pixeln erstellen

}
</script>

Wenn nichts weiter hinzukommt, wird der Canvas in der linken oberen Ecke des Browsers platziert.

Um den Canvas beliebig im Browser platzieren zu können, muss er an einen <DIV>-Container angehängt werden, dessen Position dann frei bestimmt werden kann, also:

<script>
function setup(){

var container = createCanvas(800,400); // Canvas erstellen
container.parent('p5canvas'); // Canvas an Container auf der Seite anhängen
}
</script>

und im Body der Seite wird an der gewünschten Stelle ein <DIV>-Container eingerichtet, der beliebig gestaltet werden kann (hier mit dem Namen "p5canvas"):

<DIV id="p5canvas" style="width:800;border: 1px solid #333;box-shadow: 8px 8px 5px #444;padding: 8px 12px;background-color:ffffff"></DIV>

Innerhalb des Canvas entspricht das Koordinatensystem dem des Browsers (links oben ist 0,0).

Wie in Javascript werden auch in P5 die Scripte Zeile für Zeile nacheinander ausgeführt. Jeder Befehl wird hierbei mit einem Semikolon abgeschlossen.

Man kann die Scripte normal in einem html-Editor erstellen und dann im Rahmen einer Webseite laufen lassen (z.B. mit dem Editor Brackets unter https://brackets.io/ ). Zum schnellen Ausprobieren reicht in den meisten Fällen aber auch schon der Online-P5-Editor unter https://editor.p5js.org/ aus (hier braucht man auch keine <script>...</script>-Tags).

Während in

function setup(){...}

Das Script nur einmal am Anfang ausgeführt wird, wird alles in

function draw(){...}

in einer Schleife ausgeführt, deren Framerate (Durchläufe pro Sekunde) sich via frameRate() ausgeben bzw. verändern lässt (wodurch sich z.B. die Geschwindigkeit von Anwendungen anpassen lässt), z.B. (kopieren und im https://editor.p5js.org/ ausführen):

function draw(){
background(255,255,255); // weißer Hintergrund (r,g,b)
text(frameRate(), 10, 10); // Textausgabe mit Framerate auf dem Punkt (x,y) 10,10
}

 

Die Konsole

In Browsern gibt es eine Konsole, die praktisch ist, um sich zwischendurch via print() Variablen ausgeben zu lassen oder die Stelle zu finden, an der ein Fehler auftritt. Die Konsole erreicht man in den verschiedenen Browsern folgendermaßen:

Chrome und Firefox:
F12
drücken (bzw. am Mac: [&#8984;]+[Optionstaste]+[J]).

Safari (Mac):
1. Rechte Maustaste klicken und im dortigen Menü auf "Element Information" klicken

Internet Explorer:
F12
drücken, im rechten Reiter auf "Command"-Symbol klicken

Hier kann man sich die Framerate z.B. nun mit print() über die Konsole ausgeben lassen:

function draw(){
print (frameRate()); // Textausgabe in der Konsole
}

 

Formen und Farben

Mit P5 lassen sich schnell Linien, Kreise, Qudrate und andere geometrische Formen zeichnen. Das ist besonders für die Darstellung von interaktiven Graphen spannend, aber auch für viele andere Visualisierungen.

Die hierfür zuständigen Formen sind sehr einfach aufgebaut: Es wird immer zunächst die Form beschrieben, die man zeichnen möchte, und dann die Positionen der Grenzpunkte (x,y).
Bei Rechtecken und Ellipsen wird die linke obere Ecke mit einem Punkt beschrieben (x,y) und dann die Weite und Höhe angegeben (width, height).
Beim Kreisbogen (arc) wird ebenfalls die linke obere Ecke mit einem Punkt (x,y) und die Weite und Höhe mit width und height beschrieben. Hinzu kommt noch der Start- und Endwinkel (start, stop). Will man diesen in Grad angeben, dann sollte man in der setup()-Funktion die Zeile hinzufügen: angleMode(DEGREES);


Verschiedene Grundformen (McCarthy, Reas, Fry 2015, S. 20)


Man könnte hier z.B im https://editor.p5js.org/ mit verschiedenen Formen experimentieren:

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
angleMode(DEGREES); // Winkelmaße in Grad einstellen
}
function draw(){
background(200,200,200); //grauen Hintergrund erzeugen
line (0,0, 400,400); // Linie erzeugen
triangle(50,50, 250,250, 400,50); // Dreieck erzeugen
quad(400,50, 260,260, 60,220, 310,60); // Viereck erzeugen
rect(250,250, 100,100); // Rechteck erzeugen
ellipse(250,250, 100,100); // Ellipse/Kreis erzeugen
arc(150,350, 100,100, 45,315); // Kreisbogen/Pacman erzeugen
}


Um Vielecke oder z.B. auch komplexe (Audio-)Kurven oder Spektren zu visualisieren bietet sich die Kombination aus beginShape() - vertex(x, y) - endShape() an (bzw. endShape(CLOSE), wenn die Form wieder geschlossen werden soll). Hier werden nach beginShape() mit Hilfe von vertex(x, y) eine ganze Reihe von Punkten gesetzt und mit endShape() die Reihe abgeschlossen, z.B.:

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
}
function draw(){
background(200,200,200); //grauen Hintergrund erzeugen
beginShape(); // komplexe Form beginnen;

x = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable x erzeugen
y = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable y erzeugen

vertex(x, y); // einen Punkt der Form auf den Koordinaten x,y bilden

x = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable x erzeugen
y = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable y erzeugen

vertex(x, y); // einen Punkt der Form auf den Koordinaten x,y bilden

x = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable x erzeugen
y = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable y erzeugen

vertex(x, y); // einen Punkt der Form auf den Koordinaten x,y bilden

x = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable x erzeugen
y = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable y erzeugen

vertex(x, y); // einen Punkt der Form auf den Koordinaten x,y bilden

x = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable x erzeugen
y = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable y erzeugen

vertex(x, y); // einen Punkt der Form auf den Koordinaten x,y bilden

endShape(CLOSE); // komplexe Form beenden und abschließen;
}


Mit fill(r,g,b,a) als Füllfarbe (für Flächen) und stroke(r,g,b,a) als Strichfarbe (für Linien und Buchstaben) lassen sich diese Formen farblich verändern. Es können hierfür Zahlenwerte von 0-255 für die r-, g- und b-Werte eingegeben werden sowie Zahlenwerte zwischen 0 und 100 für die Transparenz (a). Mit strokeWeight() lässt sich die Dicke von Begrenzungslinien bestimmen.
Möchte man keine Strich- oder Füllfarbe verwenden, kann man sie mit noStroke() und noFill() abschalten.

So lassen sich die zufälligen Formen (s.o.) mit zufälligen Farben und zufälliger Transparenz darstellen, z.B.:

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
}
function draw(){
background(200,200,200); //grauen Hintergrund erzeugen

r = random(255); // zufällige Zahl zwischen 0 und 255 für die Variable r erzeugen
g = random(255); // zufällige Zahl zwischen 0 und 255 für die Variable g erzeugen
b = random(255); // zufällige Zahl zwischen 0 und 255 für die Variable b erzeugen
a = random(100); // zufällige Zahl zwischen 0 und 100 für die Variable a erzeugen

fill(r,g,b,a); // Füllfarbe aus den Zufallsvariablen zusammensetzen;

r = random(255); // zufällige Zahl zwischen 0 und 255 für die Variable r erzeugen
g = random(255); // zufällige Zahl zwischen 0 und 255 für die Variable g erzeugen
b = random(255); // zufällige Zahl zwischen 0 und 255 für die Variable b erzeugen
a = random(100); // zufällige Zahl zwischen 0 und 100 für die Variable a erzeugen

stroke(r,g,b,a); // Strichfarbe aus den Zufallsvariablen zusammensetzen

s = random(10); // zufällige Zahl zwischen 0 und 10 für die Variable a erzeugen
strokeWeight(s); // Strichdicke bestimmen

beginShape(); // komplexe Form beginnen

x = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable x erzeugen
y = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable y erzeugen

vertex(x, y); // einen Punkt der Form auf den Koordinaten x,y bilden

x = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable x erzeugen
y = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable y erzeugen

vertex(x, y); // einen Punkt der Form auf den Koordinaten x,y bilden

x = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable x erzeugen
y = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable y erzeugen

vertex(x, y); // einen Punkt der Form auf den Koordinaten x,y bilden

x = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable x erzeugen
y = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable y erzeugen

vertex(x, y); // einen Punkt der Form auf den Koordinaten x,y bilden

x = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable x erzeugen
y = random(400); // zufällige Zahl zwischen 0 und 400 für die Variable y erzeugen

vertex(x, y); // einen Punkt der Form auf den Koordinaten x,y bilden

endShape(CLOSE); // komplexe Form beenden und abschließen;
}

Um schnell die richtigen Zahlenwerte für die gewünschte Farbe zu finden, eignen sich Color-Picker wie z.B. unter
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Colors/Color_picker_tool ,
https://htmlcolorcodes.com/color-picker/ oder https://fffuel.co/cccolor/ .


Will man verschiedene Formen (oder auch Bilder etc.) über den Bildschirm bewegen, so gibt es hier die Möglichkeit, einfach über die x- und y-Koordinaten eine Bewegung herzustellen, wie z.B. einen Pacman mit jedem Frame um 1 Pixel weiter nach links zu verschieben.

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
angleMode(DEGREES); // Winkelmaße in Grad einstellen
x=0; // Variable x für den Start auf 0 setzen
}

function draw(){
background(0,0,0); // schwarzen Hintergrund erzeugen
fill(255,240,25); // gelbe Füllfarbe für Pacman erzeugen
x=x+1; // x bei jedem Frame um 1 erhöhen
arc(x,200, 100,100, 45,315); // Pacman auf der jeweiligen Position von x anzeigen
}



Texte, Variablen und Operatoren

Neben Formen und Farben können natürlich auch Texte und Zahlen ausgegeben werden. Dies geschieht mit Hilfe von text('Beispieltext', x, y,width,height) an der Position x,y und optional auch in einer bestimmten Weite (width) und Höhe (height).
Die Textgröße kann dabei über textSize() bestimmt werden, der Textstil über textStyle() (mit den Einstellungen 'NORMAL', 'ITALIC', 'BOLD' und 'BOLDITALIC'), und die Textfarbe über stroke() (die Buchstabenumrisse) und fill() (die Füllfarbe, s.o.). So könnte man z.B. die x-Koordinaten von Pacman am oberen Bildrand in roter Farbe ausgeben:


function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
angleMode(DEGREES); // Winkelmaße in Grad einstellen
x=0; // Variable x für den Start auf 0 setzen
}

function draw(){
background(0,0,0); // schwarzen Hintergrund erzeugen
fill(200,50,50); // rote Füllfarbe für den Text erzeugen

textSize(20); // Textgröße einstellen (optional)
textStyle('BOLD'); // Textstil einstellen (optional)
text('X-Koordinate von Pacman: ' + x, 10,20);
// Text ausgeben

fill(255,240,25); // gelbe Füllfarbe für Pacman erzeugen
x=x+1; // x bei jedem Frame um 1 erhöhen
arc(x,200, 100,100, 45,315); // Pacman auf der jeweiligen Position von x anzeigen
}



Variablen
können Texte, ganze Zahlen (int), Dezimalzahlen (float), Listen (array) oder auch boolsche Variblen sein (true/false). Sie werden mit var deklariert.
Werden Sie innerhalb von Funktionen mit var deklariert (z.B. setup(){...} oder draw(){...}), dann gelten sie nur innerhalb der jeweiligen Funktion.
Werden sie außerhalb von Funktionen mit var deklariert, dann gelten sie für alle Bereiche.
Man kann Variablen aber auch einfach ohne Deklaration mit einem Gleicheitszeichen erstellen, dann gelten sie auch über die Funktionen hinweg.

Variablen werden via Gleichheitszeichen zugewiesen, d.h. = bedeutet nicht "ist gleich", sondern, dass das, was rechts vom Gleichheitszeichen steht, der linken Seite zugewiesen wird.
Natürlich gelten die mathematischen Operatoren (wie +, -, *, /, pow(x,y) (x hoch y), sqrt(x,y) (xte Wurzel aus y) und % (Modulo) etc.) sowie Regeln (wie z.B. Punktrechnung geht vor Strichrechnung).

Arrays sind Listen von Zahlen (getrennt mit Beistrichen, z.B. 1, 2, 3, 4, 5 ...) oder Wörtern/Texten/Buchstaben (in Apostrophen und getrennt mit Beistrichen, z.B. 'C', 'C#', 'D', 'D#', 'E', 'F', 'F#',...) und sie werden via var in eckigen Klammern deklariert, z.B.

var oktave_array = [1, 2, 3, 4, 5, 6, 7, 8]; // Array für Oktavlage anlegen
var tonhoehe_array = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'H'];
// Array für Tonhöhen anlegen
var frequenz_array = [32.7032, 34.6478, 36.7081, 38.8909, 41.2034, 43.6535, 46.2493, 48.9994, 51.9131, 55.0000, 58.2705, 61,7354];
// Array für Frequenzen anlegen



Die Reihenfolge in Arrays wird über den Index bestimmt (gezählt ab 0), so dass man hierüber auf einzelne Elemente des Arrays zugreifen kann, wie z.B. über die Mauspositon (erhältlich durch mouseX und mouseY):

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
}

function draw(){
background(200,200,200);
var oktave_array = [1, 2, 3, 4, 5, 6, 7, 8]; // Array für Oktavlage anlegen
var tonhoehe_array = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'H'];
// Array für Tonhöhen anlegen
var frequenz_array = [32.7032, 34.6478, 36.7081, 38.8909, 41.2034, 43.6535, 46.2493, 48.9994, 51.9131, 55.0000, 58.2705, 61,7354];
// Array für Frequenzen anlegen

maus_x = round(map(mouseX, 0,400, 0,11)); // x-Mausposition (mouseX) von der X-Skala des Canvas (0-400) auf die Anzahl der Elemente im tonhoehe_array (0-11) mappen, den Wert zu einer ganzen Zahl runden und der Variablen maus_X zuweisen.
maus_y = round(map(mouseY, 0,400, 7,0)); // y-Mausposition (mouseY) von der Y-Skala des Canvas (0-400) auf die Anzahl der Elemente im
oktave_array (0-7) mappen, den Wert zu einer ganzen Zahl runden und der Variablen maus_Y zuweisen.
oktavlage = oktave_array[maus_y]; // über die gemappte y-Mausposition die Position der Oktavlage im oktave_array ermitteln und an die Variable oktavlage übergeben.
oktavierung = oktavlage-1; // aus der Oktavlage den Faktor für die Oktavierung bilden und an die Variable oktavierung übergeben
tonhoehe = tonhoehe_array[maus_x]; // über die gemappte x-Mausposition die Position der Tonhöhe im tonhoehe_array ermitteln und an die Variable tonhoehe übergeben.

frequenz = round(pow(2,oktavierung) * frequenz_array[maus_x]); // über die gemappte x-Mausposition die Position der Frequenz im frequenz_array ermitteln und mit dem Quadrat des Werts in der Variable oktavierung multiplizieren und das Ergebnis an die Variable frequenz übergeben.
text('aktuelle Tonhöhe: ' + tonhoehe + oktavlage + ' (' + frequenz + ' Hz)', 10, 10); //Ausgabe von Tonhöhe und Frequenz
}

Mit Hilfe der Funktion map(Wert, start1, stop1, start2, stop2) lassen sich Wertebereiche aneinander angleichen. Das ist hilfreich, wenn z.B. 12 Tonhöhen über eine Weite von 400 Pixeln verteilt werden sollen (s.o.).

Wie in anderen Sprachen auch lassen sich über .unshift() Elemente an den Anfang eines Array hinzufügen bzw. über .pop() am Ende aus dem Array entfernen. So ließe sich z.B. eine fortlaufende Welle erzeugen, indem man zu einem Array ständig neue Zahlen hinzufügt und die gleiche Anzahl von Zahlen wieder entfernt, z.B.:



function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
noise_array=[]; // Array deklarieren
for (i = 0; i < 400; i++) { // for-Schleife starten: zähle i von 0 hoch, solange es kleiner als 400 ist.
noise_array.push(random(100,200)); // Füge eine zufällige Zahl zwischen 100 und 200 am Ende des Arrays hinzu
}
}
function draw(){
background(200,200,200); // grauen Hintergrund anzeigen
noFill(); // keine Füllfarbe
noise_array.pop(); // entferne das letzte Element aus dem Array
noise_array.unshift(random(100,200)); // füge eine zufällige Zahl zwischen 100 und 200 zum Beginn des Arrays hinzu
beginShape(); // komplexe Form beginnen
for (i = 0; i < 400; i++) { // for-Schleife starten: zähle i von 0 hoch, solange es kleiner als 400 ist.
vertex(i, noise_array[i]); // einen Punkt der Form auf den Koordinaten i und i-tes Element des Arrays bilden
}
endShape(); // komplexe Form beenden
}



Weitere Befehle zum Anhängen/Entfernen von Elementen in einem Array sind:
.pop() : entfernt das letzte Element aus einem Array
.push() : fügt ein oder mehrere Elemente ans Ende eines Arrays hinzu
.shift() : entfernt das erste Element aus einem Array
.unshift() : fügt ein oder mehrere Elemente an den Anfang eines Arrays hinzu

 

Schleifen und Entscheidungen

Besonders bei der Arbeit mit Arrays oder größeren Datenmengen benötigt man Schleifen, also Vorgänge, die so lange wiederholt werden, bis alle Variablen entsprechend gesetzt/verändert/bearbeitet worden sind.
Am prominentesten ist die for(Anfangsbedingung, Endbedingung, Zähler){...}- Schleife mit folgender Struktur

for (i = 0; i < 400; i++) { // starte bei i = 0 und solange i kleiner als 400 ist, erhöhe i um 1
// .. mache irgendwas
}


Man kann z.B. die Schleife dazu verwenden, um Elemente eines Arrays nacheinander abzuarbeiten (s.o.).

Weniger prominent und im Einsatz etwas gefährlicher ist die while(Bedingung){...}-Schleife, die so lange ausgeführt wird, so lange eine bestimmte Bedingung erfüllt ist. Das Gefährliche bei der while()-Schleife ist, dass sie keine Abbruchbedingung voraussetzt, so dass man schnell Gefahr läuft eine unendliche Schleife zu erschaffen, während bei der for()-Schleife die Abbruch- oder Endbedingung immer schon ein Teil der Ausgangsdefinition ist.
while()-Schleifen kann man als idle-Funktionen verwenden, die z.B. solange ablaufen, bis eine Maus- oder Tastatureingabe erfolgt, z.B.

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
}

function draw(){
while (mouseX>100 && mouseY>100 && mouseX<300 && mouseY<300) { // solange die Maus sich innerhalb eines Quadrats im Canvas befindet ...
background(random(255),random(255),random(255)); // ... färbe den Hintergrund zufällig ein
}
}


Für Entscheidungen werden if(Bedingung){...}else{...}-Strukturen verwendet.

if (i == 0) { // wenn die Variable i = 0 ist, dann
// .. mache irgendwas
} else { // sonst
// .. mache etwas anderes
}


Zum Testen der Bedingung in den runden Klammern stehen verschiedene Operatoren zur Verfügung:

x == y : x ist gleich y (bei if-Bedingungen werden immer doppelte Gleichheitszeichen verwendet)
x != y : x ist ungleich y
x > y : x ist größer als y
x < y : x ist kleiner als y
x >= y : x ist größer/gleich y
x <= y : x ist kleiner/gleich y

Man kann auch mehrerere Bedingungen mit den logischen Operatoren && (und) oder || (oder) miteinander verknüpfen (dies gilt auch für while()-Schleifen).
So könnte man z.B. über eine if()-else-Struktur den Mund des Pacmans sich öffnen und schließen lassen

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
angleMode(DEGREES); // Winkelmaße in Grad einstellen
mund="auf"; // Variable mund für den Pacman auf "auf" setzen
oberkiefer=315; // Winkel für den Oberkiefer auf 315° setzen
unterkiefer=45; // Winkel für den Unterkiefer auf 45° setzen
}

function draw(){
background(0,0,0); // schwarzen Hintergrund erzeugen
fill(255,240,25); // gelbe Füllfarbe für Pacman erzeugen
if (mund=="auf") { // wenn der Mund geöffnet ist ...
oberkiefer=oberkiefer+1; // addiere zum Oberkieferwinkel 1°
unterkiefer=unterkiefer-1; // subtrahiere vom Unterkieferwinkel
}
if (mund=="zu") { // wenn der Mund geöffnet ist ...
oberkiefer=oberkiefer-1; // subtrahiere vom Oberkieferwinkel
unterkiefer=unterkiefer+1; // addiere zum Unterkieferwinkel 1°
}
if (unterkiefer==0){mund="zu"} // wenn der Unterkiefer auf 0 ist, setze die Variable mund auf "zu"
if (unterkiefer==45){mund="auf"} // wenn der Unterkiefer auf 45 ist, setze die Variable mund auf "auf"

arc(200,200, 50,50, unterkiefer,oberkiefer); // zeige Pacman mit seiner jeweiligen Mundöffnung

}

 


Rotieren, Skalieren, Transponieren

Um eine Figur um ihre eigene Achse drehen zu lassen, eignet sich die rotate()-Funktion sehr gut. Der Winkel sollte hierbei via angleMode(DEGREES) in Grad eingestellt werde (da er sonst in radians berechnet wird).
Da sich bei der Rotation immer an den Koordinaten 0,0 orientiert wird (= linke obere Ecke des Canvas), sollte man diese via translate(x, y) entsprechend verschieben.
Man könnte hierüber z.B. den Pacman an die Maus anhängen und ihn je nach Position um seine eigene Achse drehen lassen, z.B.

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
angleMode(DEGREES); // Winkelmaße in Grad einstellen
mund="auf"; // Variable mund für den Pacman auf "auf" setzen
oberkiefer=315; // Winkel für den Oberkiefer auf 315° setzen
unterkiefer=45; // Winkel für den Unterkiefer auf 45° setzen
}

function draw(){
background(0,0,0); // schwarzen Hintergrund erzeugen
fill(255,240,25); // gelbe Füllfarbe für Pacman erzeugen
if (mund=="auf") { // wenn der Mund geöffnet ist ...
oberkiefer=oberkiefer+1; // addiere zum Oberkieferwinkel 1°
unterkiefer=unterkiefer-1; // subtrahiere vom Unterkieferwinkel
}
if (mund=="zu") { // wenn der Mund geöffnet ist ...
oberkiefer=oberkiefer-1; // subtrahiere vom Oberkieferwinkel
unterkiefer=unterkiefer+1; // addiere zum Unterkieferwinkel 1°
}
if (unterkiefer==0){mund="zu"} // wenn der Unterkiefer auf 0 ist, setze die Variable mund auf "zu"
if (unterkiefer==45){mund="auf"} // wenn der Unterkiefer auf 45 ist, setze die Variable mund auf "auf"
translate(mouseX, mouseY); // verlagere die Koordinaten 0,0 auf die jeweiligen Mauskoordinaten
rotate(mouseY);
// verwende die Y-Koordinate der Maus in Grad, um alles weitere zu drehen
arc(0,0, 50,50, unterkiefer,oberkiefer); // zeige Pacman mit seiner jeweiligen Mundöffnung
}


Man könnte mit der Maus-X-Koordinate gleichzeitig via scale() die Skalierung des Canvas verändern und damit je nach Mausposition den Pacman größer und kleiner werden lassen. Wenn man möchte, dass sich das Rotieren, Transponieren und Skalieren nur auf den Pacman auswirkt und nicht auf z.B. ein zusätzliches rotes Quadrat im Bild, dann kann man diese Funktionen via push() und pop() auf den Pacman beschränken, z.B.

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
angleMode(DEGREES); // Winkelmaße in Grad einstellen
mund="auf"; // Variable mund für den Pacman auf "auf" setzen
oberkiefer=315; // Winkel für den Oberkiefer auf 315° setzen
unterkiefer=45; // Winkel für den Unterkiefer auf 45° setzen
}

function draw(){
background(0,0,0); // schwarzen Hintergrund erzeugen
fill(255,240,25); // gelbe Füllfarbe für Pacman erzeugen
if (mund=="auf") { // wenn der Mund geöffnet ist ...
oberkiefer=oberkiefer+1; // addiere zum Oberkieferwinkel 1°
unterkiefer=unterkiefer-1; // subtrahiere vom Unterkieferwinkel
}
if (mund=="zu") { // wenn der Mund geöffnet ist ...
oberkiefer=oberkiefer-1; // subtrahiere vom Oberkieferwinkel
unterkiefer=unterkiefer+1; // addiere zum Unterkieferwinkel 1°
}
if (unterkiefer==0){mund="zu"} // wenn der Unterkiefer auf 0 ist, setze die Variable mund auf "zu"
if (unterkiefer==45){mund="auf"} // wenn der Unterkiefer auf 45 ist, setze die Variable mund auf "auf"
push(); // Geltungsbereich für das verschobene Koordinatensystems beginnen
translate(mouseX, mouseY); // verlagere die Koordinaten 0,0 auf die jeweiligen Mauskoordinaten
rotate(mouseY);
// verwende die Y-Koordinate der Maus in Grad, um alles weitere zu drehen
scale(mouseX/100); // Koordinatensystem um ein Hundertstel der jeweiligen X-Maus-Koordinate vergrößern/verkleinern
arc(0,0, 50,50, unterkiefer,oberkiefer); // zeige Pacman mit seiner jeweiligen Mundöffnung
pop(); // Geltungsbereich für das verschobene Koordinatensystems beenden
fill(150,50,50); // Füllfarbe auf dunkelrot setzen
rect(50,50,50,50);
// Rechteck zeichnen
}

 

Maus- und Tastatureingaben

Über mouseX und mouseY lassen sich unkompliziert die Maus-Koordinaten im aktuellen Frame abfragen, so dass man hierüber die Maus tracken kann, z.B.:


function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
fill(200,200,200); // graue Füllfarbe einstellen
noStroke(); // keine Strichfarbe
}

function draw(){
ellipse(mouseX, mouseY, 8,8); // Punkt an die Maus anhängen und mit jedem Frame einen Punkt malen

}


Über pmouseX und pmouseY lassen sich die Mauskoordinaten des jeweils vorangegangenen Frames abrufen, so dass man diese mit den aktuellen Koordinaten zu einer Linie verbinden kann. Wenn man diese Funktion davon abhängig macht, ob eine Maustaste geklickt ist oder nicht (via mouseIsPressed), kann man hierüber gezielt Linien zeichnen, z.B.

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
stroke(200,200,200); // graue Füllfarbe einstellen
}

function draw(){
if (mouseIsPressed == true) { // bei einem Mausklick:
line (mouseX, mouseY, pmouseX, pmouseY); // Linie zwischen aktueller und letzter Mausposition zeichnen
}
}


Bei der Abfrage von mouseIsPressed lässt sich mit Hilfe von mouseButton weiter abfragen, ob die linke oder rechte Maustaste gedrückt wurde. So kann z.B. über die Maustaste zwischn den Farben gewechselt werden:

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
stroke(200,200,200); // graue Füllfarbe einstellen
}

function draw(){
if (mouseIsPressed == true) { // bei einem Mausklick:
if (mouseButton == LEFT) { stroke(200,200,200); } // bei linker Maustaste: Strichfarbe wird grau
if (mouseButton == RIGHT) { stroke(150,50,50); } // bei rechter Maustaste: Strichfarbe wird rot
line (mouseX, mouseY, pmouseX, pmouseY); // Linie zwischen aktueller und letzter Mausposition zeichnen
}
}


Schieberegler (slider) sind für Mauseingaben eingaben sehr hilfreich. So lassen sich Schieberegler via createSlider(Minimum, Maximum, Defaultwert, Schrittgröße) erstellen und über .position(x,y) und .style() in ihrer Position und Größe verändern. Z.B. könnte man die Farbwerte, Transparenz und Strichdicke bei den Mauslinien via Slider einstellen


function setup(){
createCanvas(500,400); // Canvas-Größe bestimmen
rslider = createSlider(0, 255, 100, 1); //slider für rot von 0-255 erstellen
rslider.position(0, 20); // rot-slider bei 0,20 positionieren
rslider.style('width', '80px'); // Weite des Sliders bestimmen
gslider = createSlider(0, 255, 100, 1); //slider für grün von 0-255 erstellen
gslider.position(100, 20); // grün-slider bei 100,20 positionieren
gslider.style('width', '80px'); // Weite des Sliders bestimmen
bslider = createSlider(0, 255, 100, 1); //slider für blau von 0-255 erstellen
bslider.position(200, 20); // blau-slider bei 200,20 positionieren
bslider.style('width', '80px'); // Weite des Sliders bestimmen
aslider = createSlider(0, 100, 100, 1); //slider für Transparenz von 0-255 erstellen
aslider.position(300, 20); // Transparenz(alpha)-slider bei 300,20 positionieren
aslider.style('width', '80px'); // Weite des Sliders bestimmen
swslider = createSlider(0, 10, 1, 0.1); //slider für Strichdicke von 0-255 erstellen
swslider.position(400, 20); // Strichdicke-slider bei 400,20 positionieren
swslider.style('width', '80px'); // Weite des Sliders bestimmen
}

function draw(){
stroke(rslider.value(), gslider.value(), bslider.value(), aslider.value()); // Strichfarbe nach den Slidereinstellungen richten
fill(rslider.value(), gslider.value(), bslider.value(), aslider.value()); // Füllfarbe nach den Slidereinstellungen richten
strokeWeight(swslider.value()); // Strichdicke nach den Slidereinstellungen richten
if (mouseIsPressed == true) { // bei einem Mausklick:
line (mouseX, mouseY, pmouseX, pmouseY); // Linie zwischen aktueller und letzter Mausposition zeichnen
}
rect(0,0,500,20); // Rechteck als Vorschau mit der ausgewählten Farbe zeichnen
}

 

Es gibt über createInput() und createButton() auch die Möglichkeit zur Texteingabe, so dass man z.B. in dem Farbscript auch beliebige Texte beliebig einfärben kann:

function setup(){
createCanvas(500,400); // Canvas-Größe bestimmen
rslider = createSlider(0, 255, 100, 1); //slider für rot von 0-255 erstellen
rslider.position(0, 20); // rot-slider bei 0,20 positionieren
rslider.style('width', '80px'); // Weite des Sliders bestimmen
gslider = createSlider(0, 255, 100, 1); //slider für grün von 0-255 erstellen
gslider.position(100, 20); // grün-slider bei 100,20 positionieren
gslider.style('width', '80px'); // Weite des Sliders bestimmen
bslider = createSlider(0, 255, 100, 1); //slider für blau von 0-255 erstellen
bslider.position(200, 20); // blau-slider bei 200,20 positionieren
bslider.style('width', '80px'); // Weite des Sliders bestimmen
aslider = createSlider(0, 100, 100, 1); //slider für Transparenz von 0-255 erstellen
aslider.position(300, 20); // Transparenz(alpha)-slider bei 300,20 positionieren
aslider.style('width', '80px'); // Weite des Sliders bestimmen
swslider = createSlider(0, 10, 1, 0.1); //slider für Strichdicke von 0-255 erstellen
swslider.position(400, 20); // Strichdicke-slider bei 400,20 positionieren
swslider.style('width', '80px'); // Weite des Sliders bestimmen
textfenster = createInput(); // Texteingabe erstellen
textfenster.position(0,50); // Texteingabe positionieren
textfenster.style('width', '400px'); // Weite der Texteingabe bestimmen
tbutton = createButton("texteingabe"); // Texteingabebutton erstellen
tbutton.position(400, 50); // Texteingabebutton positionieren
tbutton.style('width', '100px'); // Weite des Texteingabebuttons bestimmen
tbutton.mousePressed(textausgabe);
// Funktion ausführen, wenn der Texteingabebutton geklickt wird
}

function draw(){
stroke(rslider.value(), gslider.value(), bslider.value(), aslider.value()); // Strichfarbe nach den Slidereinstellungen richten
fill(rslider.value(), gslider.value(), bslider.value(), aslider.value()); // Füllfarbe nach den Slidereinstellungen richten
strokeWeight(swslider.value()); // Strichdicke nach den Slidereinstellungen richten
if (mouseIsPressed == true) { // bei einem Mausklick:
line (mouseX, mouseY, pmouseX, pmouseY); // Linie zwischen aktueller und letzter Mausposition zeichnen
}
rect(0,0,500,20); // Rechteck als Vorschau mit der ausgewählten Farbe zeichnen
}

function textausgabe(){ // Funktion erstellen, die nach einer Texteingabe ausgeführt wird
textSize(swslider.value()*10); // Textgröße über den strokeWeight-Slider bestimmen lassen
text(textfenster.value(), random(0,400), random(50,400)); // Textausgabe an einer zufälligen Position
}


Es zeigt sich hier, dass neben setup(){...} und draw(){...} auch via
function beliebigerName(){...}
beliebige eigene Funktionen möglich sind, die die Vorgänge innerhalb und außerhalb des Canvas beeinflussen können.
Diese Funktionen werden immer mit function und dem Namen der Funktion eingeleitet, in den runden Klammern () können Variablen übergeben werden und in den geschweiften Klammen {} findet dann die weitere Verarbeitung statt.

Mit Hilfe der Library p5.touchgui (https://github.com/L05/p5.touchgui) lassen sich alle möglichen Eingabefelder, Slider, Buttons etc. beliebig gestalten

 

Neben Mauseingaben sind natürlich über die Abfrage keyIsPressed und key (bei einfachem Tastendruck) oder keyIsDown(keycode) (bei kombiniertem Tastendruck) auch Tastatureingaben möglich (die entsprechende Nummer für den keycode lässt sich schnell über z.B. https://www.toptal.com/developers/
keycode/for/alt
ermitteln). So ließe sich z.B. die Bewegung des Pacmans über die Pfeiltasten steuern und die Bewegungen des Ghosts (z.B. ein rotes Rechteck) über die Tasten A,S,D,W, während man via dist() die Distanz zwischen beiden ausrechnen kann, z.B.

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
angleMode(DEGREES); // Winkelmaße in Grad einstellen
mund="auf"; // Variable mund für den Pacman auf "auf" setzen
oberkiefer=315; // Winkel für den Oberkiefer auf 315° setzen
unterkiefer=45; // Winkel für den Unterkiefer auf 45° setzen
richtung="rechts"; // Ausgangs-Richtung festlegen
x_pac=200; // Pacman X-Koordinate in die Mitte setzen
y_pac=200; // Pacman Y-Koordinate in die Mitte setzen
x_ghost=50 // Ghost X-Koordinate in die Mitte setzen
y_ghost=50 // Ghost Y-Koordinate in die Mitte setzen
}
function draw(){
background(0,0,0); // schwarzen Hintergrund erzeugen
fill(255,240,25); // gelbe Füllfarbe für Pacman erzeugen
if (mund=="auf") { // wenn der Mund geöffnet ist ...
oberkiefer=oberkiefer+1; // addiere zum Oberkieferwinkel 1°
unterkiefer=unterkiefer-1; // subtrahiere vom Unterkieferwinkel 1°
}
if (mund=="zu") { // wenn der Mund geschlossen ist ...
oberkiefer=oberkiefer-1; // subtrahiere vom Oberkieferwinkel 1°
unterkiefer=unterkiefer+1; // addiere zum Unterkieferwinkel 1°
}
if (unterkiefer==0){mund="zu"} // wenn der Unterkiefer auf 0 ist, setze die Variable mund auf "zu"
if (unterkiefer==45){mund="auf"} // wenn der Unterkiefer auf 45 ist, setze die Variable mund auf "auf"

if (keyIsDown(RIGHT_ARROW)) { //wenn Pfeil nach rechts gedrückt ist
if(x_pac>450){x_pac=-50;} // Zurücksetzen des Pacmans, falls am Rand
x_pac=x_pac+1; // zur x-Koordinate 1 dazu addieren
}
if (keyIsDown(LEFT_ARROW)) { //wenn Pfeil nach links gedrückt ist
if(x_pac<-50){x_pac=450;} // Zurücksetzen des Pacmans, falls am Rand
x_pac=x_pac-1; // von x-Koordinate 1 abziehen
}
if (keyIsDown(DOWN_ARROW)) { //wenn Pfeil nach unten gedrückt ist
if(y_pac>450){y_pac=-50;} // Zurücksetzen des Pacmans, falls am Rand
y_pac=y_pac+1; // zu y-Koordinate 1 daz addieren
}
if (keyIsDown(UP_ARROW)) { //wenn Pfeil nach oben gedrückt ist
if(y_pac<-50){y_pac=450;} // Zurücksetzen des Pacmans, falls am Rand
y_pac=y_pac-1; // von y-Koordinate 1 abziehen
}


if (keyIsDown(87)) { //wenn w gedrückt ist
if(y_ghost<-50){y_ghost=450;} // Zurücksetzen des Ghosts, falls am Rand
y_ghost=y_ghost-1; // von y-Koordinate 1 abziehen
}
if (keyIsDown(83)) { //wenn s gedrückt ist
if(y_ghost>450){y_ghost=-50;} // Zurücksetzen des Ghosts, falls am Rand
y_ghost=y_ghost+1; // zur y-Koordinate 1 dazu addieren
}

if (keyIsDown(65)) { //wenn a gedrückt ist
if(x_ghost<-50){x_ghost=450;} // Zurücksetzen des Ghosts, falls am Rand
x_ghost=x_ghost-1; // von x-Koordinate 1 abziehen
}
if (keyIsDown(68)) { //wenn d gedrückt ist
if(x_ghost>450){x_ghost=-50;} // Zurücksetzen des Ghosts, falls am Rand
x_ghost=x_ghost+1; // zu x-Koordinate 1 daz addieren
}

arc(x_pac,y_pac, 50,50, unterkiefer,oberkiefer); // zeige Pacman mit seiner jeweiligen Mundöffnung an der Position x_pac,y_pac

fill(255,0,0); // rote Füllfarbe für Ghost erzeugen
ellipse(x_ghost, y_ghost, 50,50) // Kreis mit den x- und y-Koordinaten für den Ghost mit einer Höhe und Weite von 50 Pixeln erzeugen
abstand = dist(x_pac,y_pac,x_ghost, y_ghost); // Abstand via dist(); zwischen den x-/y-Mittelpunkten von Pacman und Ghost errechnen
if(abstand<50){ // Wenn Abstand kleiner als die Hälfte der Summe von Radius des Pacmans und des Ghosts ist
fill(255,0,0); // Füllfarbe für den Text = rot
} else {
fill(255,255,255); // Füllfarbe für den Text = weiß
}
text(round(abstand), 190,20); // Abstand (gerundet) als Text ausgeben
}

 

 

Bilder, Audio und Video

Neben Formen, Farben und Texten kann man in P5 natürlich auch mit Bildern, Klängen und Videos arbeiten. Diese Mediendateien sind manchmal etwas größer, weswegen es sich immer anbietet, diese in einer preload()-Funktion vorauszuladen.

Bilder werden über loadImage('Bilderpfad') in der preload()-Funktion einer Variablen zugeordnet, die dann im Script beliebig weiter verarbeitet werden kann. Gleiches gilt für Klänge
Man könnte z.B. anstelle eines Pacmans mit diesen zwei Autos fahren, die möglichst nicht zusammenstoßen sollen. Über die Funktion dist() kann der Abstand zwischen zwei Punkten ermittelt werden, so dass bei einem zu geringen Abstand zwischen den beiden Autos ein Unfall angezeigt wird. Dies lässt sich auch mit den entsprechenden Klängen untermalen: über
loadSound() können Klänge (Fahr- und Crashgeräusche) über die preload()-Funktion entsprechenden Variablen zugeordnet werden, die via .play(); und .stop() gestartet und gestoppt werden können. Mit Hilfe von .isPlaying() kann dabei abgefragt werden, ob ein Klang gerade spielt oder nicht.

auto1.png (122 * 271 Pixel) auto2.png (125*248)
auto1.mp3, auto2.mp3 und crash.mp3
(Die Autobilder- und Klänge können über die rechte Maustaste heruntergeladen werden)

 



Da Bilder von einer externen Quelle geladen werden, benötigt dieses Script eine eigene Server-Umgebung, um lauffähig zu sein, d.h. das Ergebnis kann man erst sehen, wenn man alles auf einen Server geladen hat. Für die Wiener Musikwissenschaft wurde deswegen unter https://muwiserver.synology.me:444 eine Testumgebung eingerichtet, in der Studierende jeweils einen Ordner mit ihrem Namen anlegen können und eigene Scripte ausprobieren können. Über das ftp-Programm FileZilla können die Daten hochgeladen werden, der Zugang ist:

Server: muwiserver.synology.me
Benutzername: Student
Passwort: [wird im Unterricht verraten]
Port: 22


<script>

var auto1; // Variable für das Autobild1 deklarieren
var auto2; // Variable für das Autobild2 deklarieren
var autoklang1; // Variable für das Auto 1 deklarieren
var autoklang2; // Variable für das Auto 1 deklarieren
var crash; // Variable für den Zusammenstoß-Klang deklarieren

function preload(){ // Bilder von ihren Pfaden vorausladen
auto1 = loadImage("auto1.png"); // Auto1-Bild einladen
auto2 = loadImage("auto2.png"); // Auto2-Bild einladen
autoklang1 = loadSound("klang/auto1.mp3"); // Auto1-Klang einladen
autoklang2 = loadSound("klang/auto2.mp3"); // Auto1-Klang einladen
crash = loadSound("klang/crash.mp3"); // Auto1-Zusammenstoß-Klang einladen
}


function setup(){
var container = createCanvas(800,800); // Canvas erstellen
container.parent('p5canvas'); // Canvas an Container auf der Seite anhängen
angleMode(DEGREES); // Winkel in ° berechnen
auto1_x=0; // Ausgangsposition X des Autos 1 festlegen
auto1_y=0; // Ausgangsposition Y des Autos 1 festlegen
auto2_x=200; // Ausgangsposition X des Autos 2 festlegen
auto2_y=0; // Ausgangsposition Y des Autos 2 festlegen
auto1_dreh=0; // Rotation des Autos 1 festlegen
auto2_dreh=0; // Rotation des Autos 2 festlegen
auto1_breite=61; // Breite des Autos 1 festlegen
auto1_laenge=136; // Länge des Autos 1 festlegen
auto2_breite=63; // Breite des Autos 2 festlegen
auto2_laenge=124; // Länge des Autos 2 festlegen
}

function draw(){
background(255,255,255); // weißen Hintergrund erzeugen
text("a = <-", 70,20); // Erklärender Text für a-Taste (links)
text("w = ^", 70,40); // Erklärender Text für w-Taste (auf)
text("s = v", 70,60); // Erklärender Text für s-Taste (ab)
text("d = ->", 70,80); // Erklärender Text für d-Taste (rechts)

text("<- = <-", 270,20); // Erklärender Text für Pfeil-links-Taste (links)
text("^ = ^", 270,40); // Erklärender Text für Pfeil-oben-Taste (auf)
text("v = v", 270,60); // Erklärender Text für Pfeil-unten-Taste (ab)
text("-> = ->", 270,80); // Erklärender Text für Pfeil-rechts-Taste (rechts)

if (keyIsPressed) { // bei Tastendruck:
if (key=='d'){ // wenn d gedrückt wurde:
if (auto1_x<850){auto1_x = auto1_x+2; // lasse Auto1 nach rechts fahren
auto1_dreh=270; // drehe es um 270°
}
}
if (key=='a'){ // wenn a gedrückt wurde:
if (auto1_x>-50){auto1_x = auto1_x-2; // lasse Auto1 nach links fahren
auto1_dreh=90; // drehe es um 90°
if (!autoklang1.isPlaying()){autoklang1.play();} // wenn Auto1-Klang gerade nicht spielt, dann spiele ihn ab.
}
}
if (key=='w'){ // wenn w gedrückt wurde:
if (auto1_y>-50){auto1_y = auto1_y-2; // lasse Auto1 nach oben fahren
auto1_dreh=180; // drehe es um 180°
if (!autoklang1.isPlaying()){autoklang1.play();} // wenn Auto1-Klang gerade nicht spielt, dann spiele ihn ab.
}
}
if (key=='s'){ // wenn s gedrückt wurde:
if (auto1_y<850){auto1_y = auto1_y+2; // lasse Auto1 nach unten fahren
auto1_dreh=0; // drehe es um 0°
if (!autoklang1.isPlaying()){autoklang1.play();} // wenn Auto1-Klang gerade nicht spielt, dann spiele ihn ab.
}
}

if (key=='ArrowRight'){ // wenn Pfeil-rechts gedrückt wurde:
if (auto2_x<850){auto2_x = auto2_x+2; // lasse Auto2 nach rechts fahren
auto2_dreh=270; // drehe es um 270°
if (!autoklang2.isPlaying()){autoklang2.play();} // wenn Auto2-Klang gerade nicht spielt, dann spiele ihn ab.
}
}
if (key=='ArrowLeft'){ // wenn Pfeil-links gedrückt wurde:
if (auto2_x>-50){auto2_x = auto2_x-2; // lasse Auto2 nach links fahren
auto2_dreh=90; // drehe es um 90°
if (!autoklang2.isPlaying()){autoklang2.play();} // wenn Auto2-Klang gerade nicht spielt, dann spiele ihn ab.
}
}
if (key=='ArrowUp'){ // wenn Pfeil-oben gedrückt wurde:
if (auto2_y>-50){auto2_y = auto2_y-2; // lasse Auto2 nach oben fahren
auto2_dreh=180; // drehe es um 180°
if (!autoklang2.isPlaying()){autoklang2.play();} // wenn Auto2-Klang gerade nicht spielt, dann spiele ihn ab.
}
}
if (key=='ArrowDown'){ // wenn Pfeil-unten gedrückt wurde:
if (auto2_y<850){auto2_y = auto2_y+2; // lasse Auto2 nach unten fahren
auto2_dreh=0; // drehe es um 0°
if (!autoklang2.isPlaying()){autoklang2.play();} // wenn Auto2-Klang gerade nicht spielt, dann spiele ihn ab.
}
}
}

if (!keyIsPressed) { // wenn keine Taste gedrückt ist
if (autoklang1.isPlaying()){autoklang1.stop();} // stoppe Auto1-Klang, falls er spielt
if (autoklang2.isPlaying()){autoklang2.stop();} // stoppe Auto2-Klang, falls er spielt
}

push(); // Änderung des Koordinatensystems für Auto1 ab hier
translate(auto1_x, auto1_y); // setze x,y=0,0 auf die Position des Autos1
rotate(auto1_dreh); // drehe Auto1 um den entsprechenden Winkel
image(auto1, -1*auto1_breite/2, -1*auto1_laenge/2, auto1_breite, auto1_laenge); //zeige das Bild Auto1 an der entsprechenden Position
pop(); // Ende der Änderung des Koordinatensystems für Auto1
push(); // Änderung des Koordinatensystems für Auto2 ab hier
translate(auto2_x, auto2_y); // setze x,y=0,0 auf die Position des Autos2
rotate(auto2_dreh); // drehe Auto2 um den entsprechenden Winkel
image(auto2, -1*auto2_breite/2, -1*auto2_laenge/2, auto2_breite, auto2_laenge); //zeige das Bild Auto2 an der entsprechenden Position
pop(); // Ende der Änderung des Koordinatensystems für Auto2
//Collision Detection mit Hilfe von dist();

distanz = round(dist(auto1_x, auto1_y, auto2_x, auto2_y)); // ermittle die Distanz über die Mittelpunkte von Auto1 (auto1_x, auto1_y) und Auto2 (auto2_x, auto2_y) und runde sie auf eine ganze Zahl.
text("Abstand: "+distanz, 70,100); // Gebe den Abstand der Mittelpunkte als Text aus
noStroke(); // keine Strichfarbe
if (auto1_dreh==0 ||auto1_dreh==180){ // wenn das Auto1 senkrecht steht
a1k_breite=auto1_breite; // weise der Variablen a1k_breite die auto1_breite zu
a1k_laenge=auto1_laenge; // weise der Variablen a1k_laenge die auto1_laenge zu
} else { // sonst:
a1k_breite=auto1_laenge; // weise der Variablen a1k_breite die auto1_laenge zu
a1k_laenge=auto1_breite; // weise der Variablen a1k_laenge die auto1_breite zu
}
if (auto2_dreh==0 ||auto2_dreh==180){ // wenn das Auto2 senkrecht steht
a2k_breite=auto2_breite; // weise der Variablen a2k_breite die auto2_breite zu
a2k_laenge=auto2_laenge; // weise der Variablen a2k_laenge die auto2_laenge zu
} else { // sonst:
a2k_breite=auto2_laenge; // weise der Variablen a2k_breite die auto2_laenge zu
a2k_laenge=auto2_breite; // weise der Variablen a2k_laenge die auto2_breite z
}
if (distanz < (a1k_laenge+a2k_laenge)/2){ // wenn die Distanz kleiner ist als der Mittelwert zwischen den beiden Längenmaßen der Autos, dann:
tint(150,0,0); // färbe die Autos rot ein
if (!crash.isPlaying()){crash.play();} // wenn das Zusammenstoßgeräusch nicht spielt, spiele es ab
} else { // sonst:
tint(255,255,255); // keine Färbung
if (crash.isPlaying()){crash.stop();} // wenn das Zusammenstoßgeräusch spielt, stoppe es.
}

}
</script>

Mit der P5-Library P5.play lassen sich Spiele dieser Art sehr viel schneller und robuster entwickeln. Hier werden besonders Collision Detection, Animation sowie Maus- und Tastatureingaben unterstützt.



Via p5.AudioIn() und p5.Amplitude() und .getLevel() lässt sich auch das Mikrofon einbinden, um Formen und Farben etc. über den Schallpegel zu verändern, z.B.:


var mikrofon; // Variable fürs den Audioeingang deklarieren
var amplitude; // Variable fürs die Amplitude deklarieren
var skalierung = 1.0; // Variable fürs die Skalierung deklarieren

function setup() {
createCanvas(400, 400); // Canvas-Größe bestimmen
background(255,255,255); // weißen Hintergrund erzeugen
mikrofon = new p5.AudioIn(); // Audio-In der Variablen mikrofon zuweisen
mikrofon.start(); // den Audio-Input starten
amplitude = new p5.Amplitude(); // den Amplituden-Detektor der Variablen amplitude zuweisen
amplitude.setInput(mikrofon); // den Audio-Input (in Form der Variablen mikrofon) dem Amplituden-Detektor zuweisen.
}

function draw() {
noStroke(); // keine Strichfarbe
fill(255, 255, 255); // weiße Füllfarbe
rect(0, 0, width, height); // Rechteck in Höhe und Breite des Canvas zeichnen
skalierung = map(amplitude.getLevel(), 0, 1.0, 10, width); // den jeweiligen Amplitudenwert (amplitude.getLevel() zwischen 0 und 1.0) auf die Größe des Canvas skalieren (zwischen 10 und width).
fill(255,0,0,10*skalierung); // rote Füllfarbe für den Kreis, Transparenz via skalierung durch die Amplitude bestimmen lassen
ellipse(width/2, height/2, skalierung, skalierung); // Kreis zeichnen, dessen Mittelpunkt in der Mitte des Canvas ist (width/2, height/2) und dessen Größe bzw. Radius von der Amplitude abhängt.
}

 

Über den Sinus-Oszllator p5.SinOsc() und der Frequenzeinstellung .freq() lassen sich beliebige Zahlen als Frequenzen hörbar machen. So wird es z.B. einfach, das Script mit den verschiedenen Tonhöhen in den verschiedenen Oktavlagen (s.o.) mit den dazugehörenden Klängen zu versehen:

 

var sinuston; // Variable für den Sinus-Oszillator erstellen

function setup(){
createCanvas(400,400); // Canvas-Größe bestimmen
sinuston = new p5.SinOsc(); // Sinus-Oszillator einbinden und der Variablen sinuston
// zuweisen

sinuston.start(); // Sinus-Oszillator über die Variable sinuston starten.
}

function draw(){
background(200,200,200);
var oktave_array = [1, 2, 3, 4, 5, 6, 7, 8]; // Array für Oktavlage anlegen
var tonhoehe_array = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'H'];
// Array für Tonhöhen anlegen
var frequenz_array = [32.7032, 34.6478, 36.7081, 38.8909, 41.2034, 43.6535, 46.2493, 48.9994, 51.9131, 55.0000, 58.2705, 61,7354];
// Array für Frequenzen anlegen

maus_x = round(map(mouseX, 0,400, 0,11)); // x-Mausposition (mouseX) von der X-Skala des Canvas (0-400) auf die Anzahl der Elemente im tonhoehe_array (0-11) mappen, den Wert zu einer ganzen Zahl runden und der Variablen maus_X zuweisen.
maus_y = round(map(mouseY, 0,400, 7,0)); // y-Mausposition (mouseY) von der Y-Skala des Canvas (0-400) auf die Anzahl der Elemente im
oktave_array (0-7) mappen, den Wert zu einer ganzen Zahl runden und der Variablen maus_Y zuweisen.
oktavlage = oktave_array[maus_y]; // über die gemappte y-Mausposition die Position der Oktavlage im oktave_array ermitteln und an die Variable oktavlage übergeben.
oktavierung = oktavlage-1; // aus der Oktavlage den Faktor für die Oktavierung bilden und an die Variable oktavierung übergeben
tonhoehe = tonhoehe_array[maus_x]; // über die gemappte x-Mausposition die Position der Tonhöhe im tonhoehe_array ermitteln und an die Variable tonhoehe übergeben.

frequenz = round(pow(2,oktavierung) * frequenz_array[maus_x]); // über die gemappte x-Mausposition die Position der Frequenz im frequenz_array ermitteln und mit dem Quadrat des Werts in der Variable oktavierung multiplizieren und das Ergebnis an die Variable frequenz übergeben.
sinuston.freq(frequenz);
text('aktuelle Tonhöhe: ' + tonhoehe + oktavlage + ' (' + frequenz + ' Hz)', 10, 10); // Ausgabe von Tonhöhe und Frequenz
}


Mit der P5.sound-Library (https://p5js.org/reference/#/libraries/p5.sound) erhält man enorme zusätzliche Möglichkeiten für den Einsatz von Klängen, die im weiteren Verlauf der Übung behandelt werden.



Über createCapture(VIDEO) lässt sich auch der Eingang der Videokamera einbinden, und die damit eingefangenen Bilder lassen sich mit verschiedenen Filtern weiter verarbeiten, z.B.

var videokamera; // Variable für den Video-Input definieren
function setup() {
createCanvas(800, 600); // Canvas-Größe bestimmen
videokamera = createCapture(VIDEO); // Video-Capture erstellen und der Variablen
// videokamera zuweisen

videokamera.hide(); // das Originalbild des Video-Captures verstecken, um darauf Filter
// anwenden zu können (s.u.)

}
function draw() {
background(255,255,255); // weißer Hintergrund
var seitenverhaeltnis = videokamera.height/videokamera.width;
// Seitenverhältnis des Kamerabilds ermitteln ...

var hoehe = width* seitenverhaeltnis; // ... und darüber die passende Höhe für das
// Videobild errechnen

image(videokamera, 0, 0, width, hoehe); // Video als Bild auf der Position 0,0
// ausgeben und mit passender Weite und Höhe ausgeben

if (keyIsPressed) { // bei Tastendruck:
if (key=='1'){filter(THRESHOLD);} // filter das Bild schwarz/weiß
if (key=='2'){filter(GRAY);} // filter das Bild grau
if (key=='3'){filter(INVERT);} // invertiere das Bild
if (key=='4'){filter(POSTERIZE, 5);} // reduziere die Farben und mache ein Poster aus
// dem Bild (Werte ab 2 aufwärts)

if (key=='5'){filter(DILATE);} // erweitere die hellen Pixel im Bild
if (key=='6'){filter(BLUR, 10);} // füge Unschärfe hinzu (Werte ab 2 aufwärts)
if (key=='7'){filter(ERODE);} // erweitere die dunklen Pixel im Bild
}
if (mouseIsPressed) { // bei Mausklick:
tint(mouseX, 255-mouseX, mouseY); // färbe das Bild entsprechend der Mausposition ein
} else { // oder ...
tint(255, 255, 255); // ... hebe die Färbung auf.
}
}



Mit einer Kombination von Mikrofon und Webcam ist es z.B. auch möglich das Bild der Webcam pegelabhängig einzufärben:

var videokamera; // Variable für den Video-Input definieren
function setup() {
createCanvas(800, 600); // Canvas-Größe bestimmen
mikrofon = new p5.AudioIn(); // Audio-In der Variablen mikrofon zuweisen
mikrofon.start(); // den Audio-Input starten
amplitude = new p5.Amplitude(); // den Amplituden-Detektor der Variablen amplitude
// zuweisen

amplitude.setInput(mikrofon);
videokamera = createCapture(VIDEO); // Video-Capture erstellen und der Variablen
// videokamera zuweisen

videokamera.hide(); // das Originalbild des Video-Captures verstecken, um darauf Filter
// anwenden zu können (s.u.)

}
function draw() {
background(255,255,255); // weißer Hintergrund
pegel=amplitude.getLevel()*5000; // Die Amplitude des Mikrofonsignals mit 5000
// multiplizieren, um in einen Bereich zwischen 0 und 255 zu kommen.

var seitenverhaeltnis = videokamera.height/videokamera.width;
// Seitenverhältnis des Kamerabilds ermitteln ...
var hoehe = width* seitenverhaeltnis; // ... und darüber die passende Höhe für das
// Videobild errechnen

image(videokamera, 0, 0, width, hoehe); // Video als Bild auf der Position 0,0
// ausgeben und mit passender Weite und Höhe ausgeben

tint(pegel+mouseX/4, pegel+mouseY/3, pegel); // färbe das Bild entsprechend der
// Mausposition und dem Mikrofonpegel ein.

}


Durch die Möglichkeiten der p5-Machine-Learning-Library (ML5 und https://ml5js.org/) lassen sich Videocapture unter P5 auch zum Motion- und Face-Tracking sowie zur Objekterkennung einsetzen.