logo CoderDojo

P5.js - Art

Kunst maken in de browser.

Introductie

Naast kunst met Scratch en Python, kan je ook kunst programmeren met Processing.

Er is een Processing variant voor in de browser. Deze heet p5.js. We gaan er in deze instructie mee aan de slag. Eerst een voorbeeld van wat je ermee kunt doen:

de code bij dit voorbeeld

Favoriet van Jaap! 😉

Kopie van Sketch 422446.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var circle = 200;
var rot;
var col;
var freq = 0.000005;
var cont = 0;
var r;

function setup() {
    createCanvas(600, 600);
}

function draw() {
    background(242);
    translate(300, 300);
    rotate(radians(rot));
    
    ellipseMode(RADIUS);
    for (var i=0; i<500; i ++) {
        circle= 200 + 50*sin(millis()*freq*i);
        col=map(circle,150,250,255,60);
        r=map(circle,150,250,5,2);
        fill(col,0,74);
        noStroke();
        ellipse(circle*cos(i), circle*sin(i),r,r);
        rot=rot+0.00005;
    }
}

Editor

p5.js is een Javascript bibliotheek. Deze kun je in elke webpagina integreren. Je kunt “sketches” schrijven in een editor op je PC en het resultaat dan bekijken in een webbrowser. Het is echter makkelijker om een editor in de browser zelf te gebruiken. Ga daarvoor naar deze editor.

De volgende instructies en voorbeelden gaan ervan uit dat je deze editor gebruikt.

De basis

p5.js sketches hebben de volgende basis:

1
2
3
4
5
6
7
function setup() {
  createCanvas(100, 100);
}

function draw() {
  background(255, 0, 200);
}

Er zijn twee functies die worden aangeroepen door de p5.js bibliotheek:

In de setup() functie zet je éénmalige instellingen, zoals bijvoorbeeld een vaste achtegrondkleur. In de draw() functie dingen die veranderen, zoals bijvoorbeeld een verschuivende kubus.

Het volgende voorbeeld tekent een draaiend vierkant:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function setup() {
    createCanvas(150, 150);
}

function draw() {
    background(255);
    translate(width / 2, height / 2);
    rotate(frameCount/50);
    rect(-26, -26, 52, 52);
}

We zullen stap voor stap door de code heen lopen:

Opdracht 1: neem de code over in de editor en kijk of er een draaiend vierkant wordt getekend door op de speel knop te klikken.
Opdracht 2: vervang waarde 255 op regel 6 eens met een andere waarde tussen 0 en 256. Wat gebeurt er met een lage waarde? En wat met een hoge?
Opdracht 3: misschien vraag je je af waarom de achtergrond iedere keer opnieuw moet worden getekend? Door // voor de regel te zetten, maak je er commentaar van en wordt het niet meer uitgevoerd. Zet // voor regel 6. Wat gebeurt er?
Opdracht 4: regel 8 zorgt ervoor dat het vierkant draait. Verander waarde 50 eens door 10. Wat gebeurt er? En bij een waarde van 100?
Opdracht 5: regel 9 tekent het vierkant. De eerste twee getallen -26 verschuiven het draaipunt van het vierkant horizontaal en verticaal. De twee laatste bepalen de hoogte en breedte. Vervang de getallen 52 eens door 75. Wat gebeurt er? En als je één van de twee 52 laat en de ander veranderd naar 75?

Na het uitvoeren van deze eerste opdrachten begrijp je een beetje hoe het werkt. In de volgende hoofdstukken gaan we verder met meer voorbeelden en uitleg.

Cirkels en muis

In dit hoofdstuk gaan we kunst maken met je muis. Beweeg je muis maar eens over het grijze vlak hieronder. 😉

Stap voor stap gaan we dit nabouwen.

Tekenvlak

We openen nu een nieuw venster om opnieuw te beginnen. Ga daarvoor naar deze editor.

We beginnen met het vlak waarin we de cirkels gaan tekenen:

1
2
3
4
5
6
7
function setup() {
    createCanvas(710, 400);
    background(102);
}

function draw() {
}

Opdracht 6: neem bovenstaande code over in de editor en voer het programma uit. Je hebt nu een grijs vlak.

Een cirkel

De volgende stap is een cirkel:

1
2
3
4
5
6
7
8
function setup() {
    createCanvas(710, 400);
    background(102);
}

function draw() {
    ellipse(300, 200, 60, 60);
}

Opdracht 7: neem regel 7 over in je code en voer het programma uit. Er verschijnt nu een witte cirkel in het grijze vlak. Wat gebeurt er als je de getallen 300 en 200 veranderd? En als je de getallen 60 veranderd?

De cirkel bewegen met de muis

Om de cirkel te bewegen met de muis, moet je er voor zorgen dat de cirkel de positie van de muis volgt:

1
2
3
4
5
6
7
8
function setup() {
    createCanvas(710, 400);
    background(102);
}

function draw() {
    ellipse(mouseX, mouseY, 60, 60);
}

Opdracht 8: vervang de getallen 300 en 200 in regel 7 met mouseX en mouseY. Deze twee variabelen bevatten de horizontale positie (mouseX) en vertikale positie (mouseY) van de muis. Voor je programma uit. Beweegt de cirkel mee?

Een kleurtje voor de cirkel

Het voorbeeld heeft een gekleurde cirkel. Laten we eens een kleurtje toevoegen:

1
2
3
4
5
6
7
8
9
function setup() {
    createCanvas(710, 400);
    background(102);
}

function draw() {
    fill(color(255, 128, 0));
    ellipse(mouseX, mouseY, 60, 60);
}

Opdracht 9: voeg regel 7 toe aan je code. Welke kleur heeft de cirkel?

Het commando fill() vult het figuur dat erna wordt getekend met de kleur die wordt bepaald door het commando color(). Het commando color() heeft 3 parameters, een voor rood, een voor groen en een voor blauw. Alle drie de kleuren kunnen met een getal tussen 0 en 255 worden bepaald. 0 is geen kleur en 255 is maximaal kleur. color(0, 0, 0) komt daarmee overeen met zwart en color(255, 0, 0) met helder rood.

Opdracht 10: speel met de kleur van de cirkel door met de getallen 255, 128 en 0 op regel 7 te variëren.

De cirkel grootte afhankelijk van de snelheid

Als je de snelheid van de muis berekent, kun je die gebruiken om de grootte van de cirkel ermee aan te passen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function setup() {
    createCanvas(710, 400);
    background(102);
}

function draw() {
    let snelheid = abs(mouseX - pmouseX) + abs(mouseY - pmouseY);
    fill(color(255, 128, 0));
    ellipse(mouseX, mouseY, snelheid, snelheid);
}

Opdracht 11: voeg regel 7 toe en pas regel 9 aan. Wordt de cirkel groter en kleiner als je je muis sneller en langzamer beweegt?

Op regel 7 wordt de snelheid van de muis berekend. Wil je daar meer over weten, lees dan door in onderstaande grijze vak.

Hoe bereken je de snelheid van de muis?

Op het moment dat je je muis beweegt, dan beweegt het horizontaal, vertikaal of in beide richtingen als je je muis schuin beweegt. Als je je muis langzaam beweegt, dan verschuift het een kleiner stukje per seconde dan als je het sneller beweegt.

De verschuiving die je doet, kun je berekenen door het verschil te bepalen tussen de vorige positie en de huidige. In p5.js geven pmouseX en pmouseY de vorige horizontale en vertikale positie en mouseX en mouseY de huidige.

De horizontale en vertikale verschuiving kun je als volgt tekenen:

vector

Een schuine beweging, zoals getekend in het plaatje, is het resultaat van een horizontale en vertikale verschuiving. De lengte van de schuine pijl is dan een maat voor de snelheid. Hoe langer de pijl, hoe groter de verschuiving en dus hoe groter de snelheid.

De code bevat een berekening voor de maat van de snelheid (niet 100% correct, maar voldoende voor dit doel). Het telt de horizontale verschuiving (abs(mouseX - pmouseX) op bij de vertikale verschuiving (abs(mouseY - pmouseY)).

De kleur afhankelijk van de snelheid

Naast de grootte van de cirkel is ook de kleur in het voorbeeld afhankelijk van de snelheid van de muis. We hebben de snelheid al berekend, nu gaat we die gebruiken bij het inkleuren van de cirkel:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function setup() {
    createCanvas(710, 400);
    background(102);
}

function draw() {
    let snelheid = abs(mouseX - pmouseX) + abs(mouseY - pmouseY);
    let kleur = color(255 - snelheid, snelheid, 128 + snelheid);
    fill(kleur);
    ellipse(mouseX, mouseY, snelheid, snelheid);
}

Met het commando color op regel 8 kunnen we een kleur maken. Het commando heeft 3 parameters. Het eerste bepaald de hoeveelheid rood (R), het tweede de hoeveelheid groen (G) en het derde en laatste de hoeveelheid blauw (B). Deze RGB waarde zorgt samen een mengsel van de drie kleuren. Daarbij zorgt color(255, 255, 255) voor wit (alle kleuren maximaal) en color(0, 0, 0) voor zwart (alle kleuren uit).

In het voorbeeld wordt voor rood de snelheid van 255 afgetrokken. Dus, hoe sneller de muis beweegt, hoe minder rood er in de kleur zit. Bij de middelste kleur, groen, is hoeveelheid direct afhankelijk van de snelheid. Hoe sneller, hoe meer groen er in de kleur zit. Tenslotte zit er bij blauw een minimum van 128 in de kleur en neemt de hoeveelheid blauw toe als de snelheid van de muis toeneemt.

Tenslotte wordt de cirkel ingekleurd met het commando fill(kleur).

Opdracht 12: wissel de berekening per kleur eens met een andere kleur. Dus bijvoorbeeld 255 - snelheid voor groen in plaats van voor rood. Welke kleuren krijg je?

Games met P5.js

We hebben gezien dat we hele mooie kunst kunnen maken. Ook hebben we programma’s gemaakt waar we zelf iets kunnen besturen, bijvoorbeeld met de muis. Nu hebben we alle ingrediënten om ook games te gaan bouwen!

In de volgende instructies ga je een spel maken waar je op doelwitten moet klikken. De doelwitten verdwijnen langzaam, en als je te langzaam bent verlies je. Het doel is om zoveel mogelijk doelwitten aan te klikken voordat je verliest!

Onderstaand een demo van de game:

Opdracht 13 We beginnen met het maken van een canvas van 400 bij 400 pixels.

1
2
3
4
5
6
7
function setup() {
    createCanvas(400, 400);
}

function draw() {
    background(255);
}

Opdracht 14 Om het spel goed te laten verlopen, moeten we verschillende gegevens bijhouden. Zo willen we bijvoorbeeld weten hoeveel levens de speler nog heeft, zodat we later kunnen bepalen of het spel over is. Daarnaast houden we bij hoe snel de doelwitten krimpen en welke doelwitten we allemaal hebben.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
let doelwitten = []
let levens = 5;
let spelerIsDood = false;
let krimpSnelheid = 0.1;

function setup() {
  createCanvas(400, 400);
}
function draw() {
    background(255);
}

Opdracht 15 Het is handig als de speler weet hoeveel levens hij nog heeft. Laten we dat toevoegen. De tekst wordt 20 pixels van links, en 20 pixels vanaf boven getekend. Probeer de tekst zelf eens te verplaatsen naar een andere hoek.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
let doelwitten = []
let levens = 5;
let spelerIsDood = false;
let krimpSnelheid = 0.1;

function setup() {
  createCanvas(400, 400);
}
function draw() {
    background(255);
    text("Levens: "+str(levens), 20, 20, 100, 100);
}

Opdracht 16 Laten we wat doelwitten te voorschijn toveren. Onze doelwitten worden cirkels. Om cirkels te tekenen, kunnen we de ellipse functie gebruiken.

We hebben eerder al een lijst met doelwitten gemaakt: hier zorgt de eerste regel voor. We gaan nu elk doelwit in de lijst met doelwitten af en tekenen ze op het scherm. Dit doen wij met een for-loop.

Je code zal er (ongeveer) uit zien als volgt. Dit is een lastig onderdeel van de opdracht, dus als je hier vragen over hebt, stel deze dan vooral!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
let doelwitten = []
let levens = 5;
let spelerIsDood = false;
let krimpSnelheid = 0.1;

function setup() {
  createCanvas(400, 400);
}
function draw() {
    background(255);
    text("Levens: "+str(levens), 20, 20, 100, 100);

    for (var i = doelwitten.length-1; i >= 0; i--) {
        t = doelwitten[i];
        
        //Teken alle doelwitten
        ellipse(t.x, t.y, t.d, t.d);    
    }  
}

Opdracht 17 Je zult zien dat er nog niks getekend wordt. Dat komt omdat we nog geen doelwitten hebben toegevoegd aan onze lijst. We kunnen de doelwitten op onderstaande manier toevoegen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let doelwitten = []
let levens = 5;
let spelerIsDood = false;
let krimpSnelheid = 0.1;

function setup() {
  createCanvas(400, 400);

 //voeg begin doelwitten toe
  doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
  doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
  doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
  doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
  doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
}
function draw() {
    background(255);
    text("Levens: "+str(levens), 20, 20, 100, 100);

    for (var i = doelwitten.length-1; i >= 0; i--) {
        t = doelwitten[i];
        
        //Teken alle doelwitten
        ellipse(t.x, t.y, t.d, t.d);    
    }  
}

Opdracht 18 Cool! Maar er gebeurt nog steeds niks. We voegen een regel toe zodat de doelwitten langzaam krimpen.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
function draw() {
    background(255);
    text("Levens: "+str(levens), 20, 20, 100, 100);

    for (var i = doelwitten.length-1; i >= 0; i--) {
        t = doelwitten[i];
        
        //Teken alle doelwitten
        ellipse(t.x, t.y, t.d, t.d);    

        //Krimp alle doelwitten
        t.d -= krimpSnelheid; 
    }  
}

Opdracht 19 Als je het programma nu draait en lang genoeg wacht, dan zullen de cirkels verdwijnen. Maar wacht eens: ze komen daarna weer terug?!

Dit gebeurt omdat de grootte van de cirkels negatief wordt na een tijdje. De computer interpreteert dit echter als een positief getal. Omdat dit niet de bedoeling van het spel is, moeten we een doelwit weggooien als het te klein is om nog verder te kunnen krimpen.

Dan doen we met onderstaande code:

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function draw() {
    background(255);
    text("Levens: "+str(levens), 20, 20, 100, 100);

    for (var i = doelwitten.length-1; i >= 0; i--) {
        t = doelwitten[i];
        
        //Teken alle doelwitten
        ellipse(t.x, t.y, t.d, t.d);    

        //Krimp alle doelwitten
        t.d -= krimpSnelheid; 

        //verwijder te kleine doelwitten
        if(t.d < 0) { //Is de grootte kleiner dan 0? Dan verwijderen
            doelwitten.splice(i, 1); //Haal het doelwit uit de lijst.
            doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
        }
    }  
}

Opdracht 20 In deze stap programmeren we dat we op doelwitten kunnen klikken, om deze te verwijderen. Dat doen we als volgt: De functie mouseClicked() activeert wanneer je klikt. Plaats deze functie helemaal onderaan in het bestand. Probeer elke regel te begrijpen en vraag om hulp als er iets onduidelijk is!

38
39
40
41
42
43
44
45
46
47
48
49
50
51
function mouseClicked() {
  for (var i = doelwitten.length-1; i >= 0; i--) {
    t = doelwitten[i];
    
    //Bereken de afstand tussen de muis en het doelwit
	let afstand = dist(mouseX, mouseY, t.x, t.y);

    if(afstand < t.d/2) { //Als de afstand kleiner is dan de halve diameter (radius) van het doelwit is het raak
      doelwitten.splice(i, 1); //Verwijder het doelwit dat geraakt is
      doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
      return;
    }
  }
}

Opdracht 21 Wat nu? De speler kan nog niet verliezen. We voegen toe dat de speler een leven verliest als een doelwit verdwijnt waar hij niet op klikt.

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function draw() {
    background(255);
    text("Levens: "+str(levens), 20, 20, 100, 100);

    for (var i = doelwitten.length-1; i >= 0; i--) {
        t = doelwitten[i];
        
        //Teken alle doelwitten
        ellipse(t.x, t.y, t.d, t.d);    

        //Krimp alle doelwitten
        t.d -= krimpSnelheid; 

        //verwijder te kleine doelwitten
        if(t.d < 0) { //Is de grootte kleiner dan 0? Dan verwijderen
            doelwitten.splice(i, 1); //Haal het doelwit uit de lijst.
            doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
            levens--;
        }
    }  
}
Opdracht 22 Als laatste: we laten de speler weten wanneer hij verloren heeft, via de volgende code:

16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
function draw() {
    background(255);
    text("Levens: "+str(levens), 20, 20, 100, 100);

    if(spelerIsDood) {
        textSize(52);
        text("Dood!", 130, 170, 100, 100);
        textSize(32);
        return; 
    }

    for (var i = doelwitten.length-1; i >= 0; i--) {
        t = doelwitten[i];
        
        //Teken alle doelwitten
        ellipse(t.x, t.y, t.d, t.d);    

        //Krimp alle doelwitten
        t.d -= krimpSnelheid; 

        //verwijder te kleine doelwitten
        if(t.d < 0) { //Is de grootte kleiner dan 0? Dan verwijderen
            doelwitten.splice(i, 1); //Haal het doelwit uit de lijst.
            doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
            levens--;
        }
    }  

    if(levens <= 0) {
        spelerIsDood = true;
    }
}

Afronden Als het goed is heb je nu een spel gemaakt. Werkt het niet? Je kan jouw code vergelijken met de uitwerking hieronder.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
let doelwitten = []
let levens = 5;
let spelerIsDood = false;
let krimpSnelheid = 0.1;

function setup() {
  createCanvas(400, 400);

 //voeg begin doelwitten toe
  doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
  doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
  doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
  doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
  doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
}

function draw() {
    background(255);
    text("Levens: "+str(levens), 20, 20, 100, 100);

    if(spelerIsDood) {
        textSize(52);
        text("Dood!", 130, 170, 100, 100);
        textSize(32);
        return; 
    }

    for (var i = doelwitten.length-1; i >= 0; i--) {
        t = doelwitten[i];
        
        //Teken alle doelwitten
        ellipse(t.x, t.y, t.d, t.d);    

        //Krimp alle doelwitten
        t.d -= krimpSnelheid; 

        //verwijder te kleine doelwitten
        if(t.d < 0) { //Is de grootte kleiner dan 0? Dan verwijderen
            doelwitten.splice(i, 1); //Haal het doelwit uit de lijst.
            doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
            levens--;
        }
    }  

    if(levens <= 0) {
        spelerIsDood = true;
    }
}
	
function mouseClicked() {
  for (var i = doelwitten.length-1; i >= 0; i--) {
    t = doelwitten[i];
    
    //Bereken de afstand tussen de muis en het doelwit
	let afstand = dist(mouseX, mouseY, t.x, t.y);

    if(afstand < t.d/2) { //Als de afstand kleiner is dan de halve diameter (radius) van het doelwit is het raak
      doelwitten.splice(i, 1); //Verwijder het doelwit dat geraakt is
      doelwitten.push({x: random(300), y: random(300), d: 50}) //Voeg een doelwit toe op een nieuwe willekeurige plek.
      return;
    }
  }
}

Verbeter het spel

We hebben zojuist een spel gemaakt. Kan jij het spel verbeteren? Probeer de volgende ideeën toe te voegen:

Tot slot

De instructie geeft je slechts een introductie van wat er mogelijk is met de p5.js bibliotheek. Naast tekenen kun je ook met geluid werken, of met foto’s en filmpjes.

Zie https://p5js.org/examples/ voor een overzicht van voorbeelden.

Een aantal voorbeelden die we zelf leuk vinden:

Licentie

Deze instructies worden, net als alle andere instructies van CoderDojo Nijmegen, aangeboden onder een Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International Licentie.

Creative Commons License