2

Recently I stumbled upon this Paintball animation from around seven years ago:

https://www.khanacademy.org/computer-programming/paintball-painter/5316001292353536

It seems that this was build with ProcessingJS which is currently no longer maintained. Thus why I want to try run it in vanilla javascript again.

When I look at the code in the link - also posted here, I think the only think I need to change or code myself is the framerate(30) at the start? When I looked for built in API for framerate I found requestAnimationFrame() in this SO answer. Is this the way to go? I hoped to find the implementation of it in the documenentation but thats not the case.

//Select your color on the bottom.
//click anywhere to shoot paintballs!

//This program works with modified canvas size. Just make sure you set this variable to the height
var canvasHeight = 400;

//https://www.khanacademy.org/computer-programming/paintball-painter/5316001292353536?width=800&height=600


//0pink, 1red, 2orange, 3yellow, 4green, 5cyan, 6blue, 7purple, 8magenta, 9grey, 10black, 11random
frameRate(30);
var splatDiameter = 40;
var numSpots = 6;
var pcolor = 11;
var pballFrames = [1234, 1234];
var pballXs = [1234, 1234];
var pballYs = [1234, 1234];
var pballColors = [1234, 1234];
var i = 0;
var j = 0;
var k = 0;
var spotXs = [1234, 1234];
var spotYs = [1234, 1234];
var spotColors = [1234, 1234];
var spotTrans = [1234, 1234];
var spotDiameters = [1234, 1234];
canvasHeight -=20;

var drawColorSelector = function(){
noStroke();
fill(255, 100, 130);
rect(0, canvasHeight, 33, 20);
fill(255, 0, 0);
rect(33, canvasHeight, 33, 20);
rect(363, canvasHeight, 37, 3);
fill(255, 140, 0);
rect(66, canvasHeight, 33, 20);
rect(363, canvasHeight + 3, 37, 3);
fill(255, 255, 0);
rect(99, canvasHeight, 33, 20);
rect(363, canvasHeight + 6, 37, 3);
fill(0, 200, 0);
rect(132, canvasHeight, 33, 20);
rect(363, canvasHeight + 9, 37, 3);
fill(0, 150, 150);
rect(165, canvasHeight, 33, 20);
fill(0, 0, 255);
rect(198, canvasHeight, 33, 20);
rect(363, canvasHeight + 12, 37, 3);
fill(150, 0, 150);
rect(231, canvasHeight, 33, 20);
rect(363, canvasHeight + 15, 37, 3);
fill(255, 0, 255);
rect(264, canvasHeight, 33, 20);
fill(150, 150, 150);
rect(297, canvasHeight, 33, 20);
rect(363, canvasHeight + 18, 37, 3);
fill(0, 0, 0);
rect(330, canvasHeight, 33, 20);
textSize(18);
text("?", 377, canvasHeight + 15);
noFill();
stroke(255, 255, 255);
strokeWeight(4);
if(pcolor < 11){
    rect(33 * pcolor, canvasHeight, 33, 20);
}else{
    rect(364, canvasHeight, 35, 20);
} 

};
var selectColor = function(){
    if(mouseIsPressed){
    if(mouseY > canvasHeight){
        if(mouseX < 33){
            pcolor = 0;
        }else if(mouseX < 66){
            pcolor = 1;
        }else if(mouseX < 99){
            pcolor = 2;
        }else if(mouseX < 132){
            pcolor = 3;
        }else if(mouseX < 165){
            pcolor = 4;
        }else if(mouseX < 198){
            pcolor = 5;
        }else if(mouseX < 231){
            pcolor = 6;
        }else if(mouseX < 264){
            pcolor = 7;
        }else if(mouseX < 297){
            pcolor = 8;
        }else if(mouseX < 330){
            pcolor = 9;
        }else if(mouseX < 363){
            pcolor = 10;
        }else{
            pcolor = 11;
        }
    }
}
};

var setFill = function(clr, trans){
    if(clr === 0){
        fill(255, 100, 130, trans);
    }else if(clr === 1){
        fill(255, 0, 0, trans);
    }else if(clr === 2){
        fill(255, 140, 0, trans);
    }else if(clr === 3){
        fill(255, 255, 0, trans);
    }else if(clr === 4){
        fill(0, 200, 0, trans);
    }else if(clr === 5){
        fill(0, 150, 150, trans);
    }else if(clr === 6){
        fill(0, 0, 255, trans);
    }else if(clr === 7){
        fill(150, 0, 150, trans);
    }else if(clr === 8){
        fill(255, 0, 255, trans);
    }else if(clr === 9){
        fill(150, 150, 150, trans);
    }else if(clr === 10){
        fill(0, 0, 0, trans);
    }else{
        fill(random(0, 255), random(0, 255), random(0, 255), trans);
    }
};

var drawPaintball = function(x, y, frames){
    if(frames !== 1234){
        noStroke();
        ellipse(x, y, (11 - frames) * 3, (11 - frames) * 3);
    }
};

var spawnPaintball = function(){
    if(mouseIsPressed && (mouseY < canvasHeight || mouseX > 400)){
        for(i = 0; i < pballXs.length + 1; i += 1){
            if(pballXs[i] === 1234 || ! pballXs[i]){
                pballXs[i] = mouseX;
                pballYs[i] = mouseY;
                if(pcolor === 11){
                    pballColors[i] = floor(random(0, 10));
                }else{
                    pballColors[i] = pcolor;
                }
            pballFrames[i] = 1;
            i = pballXs.length + 2;
    }
}

}
};

var spawnSplat = function(x, y, dia, num, pclrid){
    for(k = 0; k < num; k += 1){
            j = spotXs.length;
            if( ! spotXs[j] || spotXs[j] === 1234){
               if(k === 0){
                   spotXs[j] = x;
                   spotYs[j] = y;
                   spotDiameters[j] = 30;
                   spotColors[j] = pclrid;
                   spotTrans[j] = 255;
               }else{
                   spotXs[j] = x + random(-dia/2, dia/2);
                   spotYs[j] = y + random(-dia/2, dia/2);
                   spotDiameters[j] = random(5, 40);
                   spotColors[j] = pclrid;
                   spotTrans[j] = 255;
               } 
            j = spotXs.length + 2;
            
            }
        
    }
};

var updatePaintball = function(){
    for(i = 0; i < pballXs.length; i += 1){
    if(pballXs[i] !== 1234 && pballXs[i]){
        setFill(pballColors[i], 1000);
        drawPaintball(pballXs[i], pballYs[i], pballFrames[i]);
        pballFrames[i] += 0.4;
        
        if(pballFrames[i] > 11){
            spawnSplat(pballXs[i], pballYs[i], splatDiameter, numSpots, pballColors[i]);
            pballFrames[i] = 1234;
            pballXs[i] = 1234;
            pballYs[i] = 1234;
            pballColors[i] = 1234;
        }
    }
}
};

var updateSpots = function(){
    for(i = 0; i < spotXs.length; i += 1){
    if(spotXs[i] !== 1234 && spotXs[i]){
        noStroke();
        setFill(spotColors[i], spotTrans[i]);
        spotTrans[i] -= 0.75;
        ellipse(spotXs[i], spotYs[i], spotDiameters[i], spotDiameters[i]);
        if(spotTrans[i] < 0){
            spotXs[i] = 1234;
            spotYs[i] = 1234;
            spotTrans[i] = 1234;
            spotDiameters[i] = 1234;
        }
    }
}
};


var draw = function() {
background(255, 255, 255);
selectColor();
spawnPaintball();
updateSpots();
updatePaintball();
drawColorSelector();
};

Now it seems after some reasearch that I probably just need to replace frameRate(30) with an own implementation in toder to not be dependent on a npm package. I looked at the old processingJS documentation but I could not find how they implemented the framerate() so I could see their implementation as I am unsure how to implement this function so the Paintball effect can work.

2 Answers2

1

I would recommend p5.js.

p5.js is an interpretation of Processing for today’s web.

(editor, creative community)

It also includes the frameRate function.

If you are interested in the internals or want to replicate them in vanilla js: frameRate is located in environment.js. It affects how often the main animation loop (which is driven by requestAnimationFrame) triggers drawing.

edit: Here is a reputable example (lots of upvotes) how to throttle requestAnimationFrame to a specific frame rate.

edit: Your code works pretty much out of the box with p5.js. All you need to do is to add a setup function that initializes the canvas, and the call to frameRate also needs to be located there:

function setup() {
  createCanvas(400, 400);
  frameRate(30);
}

//insert your code

edit: To get started with p5.js, put your code in a file sketch.js and use the following HTML:

<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/p5@1.4.2/lib/p5.js"></script>
    <script src="sketch.js"></script>
  </head>
  <body>
    <main>
    </main>
  </body>
</html>

More instructions are available on the p5.js Get Started page.

Knight Industries
  • 1,325
  • 10
  • 20
  • Hey, sorry I guess I am missing something with p5.js but when I add the setup function that initializes the canvas and the call to framerate its not working out of the box? https://codesandbox.io/s/dreamy-grass-qeb0if?file=/src/index.js And thanks for the tip where frameRate is located, I will dig into it! –  Oct 10 '22 at 22:41
  • 1
    added info on getting started. see your code working [here](https://jsbin.com/huyuzaroru/1/edit?html,js,output). – Knight Industries Oct 11 '22 at 08:16
0

It is very simple to port this ProcessingJS script to p5.js:

https://editor.p5js.org/leftium/sketches/dKt3QEGFJ

Basically, replace framerate(30) with the following code:

function setup() {
  createCanvas(400, 400);
  frameRate(30);
}

The script will actually work without the call to frameRate(30). It will just run faster at the default 60 FPS.


To help you rewrite in vanilla JS without p5.js:

Leftium
  • 16,497
  • 6
  • 64
  • 99