Need help optimizing my Javascript for canvas rendering - javascript

I've written this quick script for random moving lines for the background for my portfolio. It works smoothly alone but when I start working with other CSS animations and stuff, there's a frame drop at the beginning (later it runs smooth). At least on my PC, struggles on low-end PC.
Some tips to optimize it would be helpful.
Here's my code:
/*Random Line Render Script aka Mini Browser Crasher */
/*XD Can't Revise This Script. Rofl Drains Memory. May crash low-end pc :V */
var c = document.getElementById("graph");
var dimension = [document.documentElement.clientWidth, document.documentElement.clientHeight];
c.width = dimension[0];
var ctx = c.getContext("2d");
var ctx2 = c.getContext("2d");
var posx = [100, 200, 150, 100, 0];
var posy = [100, 200, 300, 100, -100];
var posx2 = [600, 400, 200, 600];
var posy2 = [500, 200, 100, 150, 500, 500];
var posx3 = [];
var posy3 = [];
/*Generate random values for array( random starting point ) */
for (var i = 0; i < 2; i++) {
posx2.push(500 + Math.round(Math.random() * 700));
posy2.push(Math.round(Math.random() * 900));
}
for (var i = 0; i < 5; i++) {
posx3.push(1000 + Math.round(Math.random() * 300));
posy3.push(0 + Math.round(Math.random() * 1000));
}
var posx_len = posx.length;
var posx2_len = posx2.length;
var posx3_len = posx3.length;
var xa, ya;
var opa = 1;
var amount = 0.01;
var sinang = 0;
var distance1 = 0;
var distance2 = 0;
document.body.addEventListener('mousemove', (function(event) {
xa = event.clientX;
ya = event.clientY;
}));
/*Render Lines */
function draw() {
ctx.clearRect(0, 0, 10000, 10000);
ctx.beginPath();
ctx.moveTo(posx[0], posy[0]);
for (var i = 0; i < posx_len; i++) {
ctx.lineTo(posx[i], posy[i]);
ctx.strokeStyle = 'rgba(255,255,255,' + opa + ')';
ctx.stroke();
ctx.arc(posx[i], posy[i], 5, 0, 2 * Math.PI, false);
}
if (opa > 1) {
amount = -0.01 * Math.random();
}
if (opa < 0) {
amount = 0.01 * Math.random();
}
opa = opa + amount;
ctx.moveTo(posx2[0], posy2[0]);
for (var i = 0; i < posx2_len; i++) {
ctx.lineTo(posx2[i], posy2[i]);
ctx.strokeStyle = 'rgba(255,255,255,' + opa + ')';
ctx.stroke();
ctx.arc(posx2[i], posy2[i], 5, 0, 2 * Math.PI, false);
}
ctx.moveTo(posx3[0], posy3[0]);
for (var i = 0; i < posx3_len; i++) {
ctx.lineTo(posx3[i], posy3[i]);
ctx.strokeStyle = 'rgba(255,255,255,' + opa + ')';
ctx.stroke();
ctx.arc(posx3[i], posy3[i], 5, 0, 2 * Math.PI, false);
}
sinang = sinang + 0.01;
/*Frame Render Ends here*/
/*Calculation for next frame*/
for (var i = 0; i < posx_len; i++) {
posx[i] = posx[i] + (Math.cos(sinang) * i) / 2; /* Sin curve for smooth value transition. Smooth assss Butter */
posy[i] = posy[i] + (Math.cos(sinang) * i) / 2;
/* Can't believe Distance Formula is useful ahaha */
distance1 = Math.sqrt(Math.pow((posx[i] - xa), 2) + Math.pow((posy[i] - ya), 2));
if (distance1 <= 500) {
ctx.moveTo(posx[i], posy[i]);
ctx.lineTo(xa, ya);
}
for (var j = 0; j < posx2_len; j++) {
distance12 = Math.sqrt(Math.pow((posx[i] - posx2[j]), 2) + Math.pow((posy[i] - posy2[j]), 2));
if (distance12 <= 500) {
ctx.moveTo(posx[i], posy[i]);
ctx.lineTo(posx2[j], posy2[j]);
}
}
for (var j = 0; j < posx3_len; j++) {
distance13 = Math.sqrt(Math.pow((posx[i] - posx3[j]), 2) + Math.pow((posy[i] - posy3[j]), 2));
if (distance13 <= 500) {
ctx.moveTo(posx[i], posy[i]);
ctx.lineTo(posx3[j], posy3[j]);
}
}
}
posx[posx.length - 1] = posx[0];
posy[posy.length - 1] = posy[0];
/*Repeat Above Steps. Should have done this in Multi-dimensional array. Ugh I feel sad now*/
for (var i = 0; i < posx2_len; i++) {
posx2[i] = posx2[i] + (Math.sin(sinang) * i) / 2;
posy2[i] = posy2[i] - (Math.sin(sinang) * i) / 2;
distance2 = Math.sqrt(Math.pow((posx2[i] - xa), 2) + Math.pow((posy2[i] - ya), 2));
if (distance2 <= 500) {
ctx.moveTo(posx2[i], posy2[i]);
ctx.lineTo(xa, ya);
}
for (var j = 0; j < posx3_len; j++) {
distance22 = Math.sqrt(Math.pow((posx2[i] - posx3[j]), 2) + Math.pow((posy2[i] - posy3[j]), 2));
if (distance22 <= 500) {
ctx.moveTo(posx2[i], posy2[i]);
ctx.lineTo(posx3[j], posy3[j]);
}
}
}
posx2[posx2.length - 1] = posx2[0];
posy2[posy2.length - 1] = posy2[0];
for (var i = 0; i < posx3_len; i++) {
posx3[i] = posx3[i] - (Math.sin(sinang) * i) / 1.2;
posy3[i] = posy3[i] - (Math.sin(sinang) * i) / 1.2;
distance2 = Math.sqrt(Math.pow((posx3[i] - xa), 2) + Math.pow((posy3[i] - ya), 2));
if (distance2 <= 500) {
ctx.moveTo(posx3[i], posy3[i]);
ctx.lineTo(xa, ya);
}
}
posx3[posx3.length - 1] = posx3[0];
posy3[posy3.length - 1] = posy3[0];
ctx.restore();
ctx.stroke();
window.requestAnimationFrame(draw);
}
window.requestAnimationFrame(draw);
body {
background: #1f1f1f;
}
<canvas height="1080px" width="1100px" id="graph">
</canvas>

So, what I've done is used square colliders instead of circular(distance formula) and it has faster runtime now. (not much but still)
<html>
<head>
</head>
<style>
body{
background: #1f1f1f;
}
canvas{
}
</style>
<body >
<canvas height="1080px" width="1100px" id="graph">
</canvas>
</body>
<script>
/*Random Line Render Script aka Mini Browser Crasher */
/*XD Can't Revise This Script. Rofl Drains Memory. May crash low-end pc :V */
var c = document.getElementById("graph");
var dimension = [document.documentElement.clientWidth, document.documentElement.clientHeight];
c.width = dimension[0];
var ctx = c.getContext("2d");
var posx = [100,200,150,100,0];
var posy = [100,200,300,100,-100];
var posx2 = [600,400,200,600];
var posy2 = [500,200,100,150,500,500];
var posx3 = [];
var posy3 = [];
/*Generate random values for array( random starting point ) */
for(var i=0; i<2;i++){
posx2.push(500+Math.round(Math.random()*700));
posy2.push(Math.round(Math.random()*900));
}
for(var i=0; i<5;i++){
posx3.push(1000+Math.round(Math.random()*300));
posy3.push(0+Math.round(Math.random()*1000));
}
var posx_len = posx.length;
var posx2_len = posx2.length;
var posx3_len = posx3.length;
var xa,ya;
var opa =1;
var amount = 0.01;
var sinang = 0;
var distance1 = 0;
var distance2 = 0;
var t1, t2;
document.body.addEventListener('mousemove', (function (event) {
xa = event.clientX;
ya = event.clientY;
}));
/*Render Lines */
function draw(){
t1 =performance.now();
ctx.clearRect(0, 0, 1920,1080);
ctx.beginPath();
ctx.moveTo(posx[0], posy[0]);
for(var i= 0; i<posx_len;i++){
ctx.lineTo(posx[i], posy[i]);
ctx.arc(posx[i],posy[i], 5, 0, 2 * Math.PI, false);
}
if(opa>1){
amount = -0.01*Math.random();
}
if(opa<0){
amount =0.01*Math.random();
}
opa =opa +amount;
ctx.moveTo(posx2[0], posy2[0]);
for(var i = 0; i<posx2_len;i++){
ctx.lineTo(posx2[i], posy2[i]);
ctx.arc(posx2[i],posy2[i], 5, 0, 2 * Math.PI, false);
}
ctx.moveTo(posx3[0], posy3[0]);
for(var i = 0; i<posx3_len;i++){
ctx.lineTo(posx3[i], posy3[i]);
ctx.arc(posx3[i],posy3[i], 5, 0, 2 * Math.PI, false);
}
sinang = sinang+0.01;
/*Frame Render Ends here*/
/*Calculation for next frame*/
for(var i = 0;i<posx_len;i++){
posx[i] = posx[i]+ (Math.cos(sinang)*i)/2;/* Sin curve for smooth value transition. Smooth assss Butter */
posy[i] = posy[i]+ (Math.cos(sinang)*i)/2;
/* Can't believe Distance Formula is useful ahaha */
if(Math.abs(posx[i]-xa)<500 && Math.abs(posy[i]-ya)<500){
ctx.moveTo(posx[i],posy[i]);
ctx.lineTo(xa, ya);
}
for(var j = 0;j<posx2_len;j++){
if(Math.abs(posx[i]-posx2[j])<500 && Math.abs(posy[i]-posy2[j])<500){
ctx.moveTo(posx[i],posy[i]);
ctx.lineTo(posx2[j], posy2[j]);
}
}
for(var j = 0;j<posx3_len;j++){
if(Math.abs(posx[i]-posx3[j])<500 && Math.abs(posy[i]-posy3[j])<500){
ctx.moveTo(posx[i],posy[i]);
ctx.lineTo(posx3[j], posy3[j]);
}
}
}
posx[posx.length-1]=posx[0];
posy[posy.length-1] = posy[0];
/*Repeat Above Steps. Should have done this in Multi-dimensional array. Ugh I feel sad now*/
for(var i = 0;i<posx2_len;i++){
posx2[i] = posx2[i]+ (Math.sin(sinang)*i)/2;
posy2[i] = posy2[i]-(Math.sin(sinang)*i)/2;
if(Math.abs(posx2[i]-xa)<500 && Math.abs(posy2[i]-ya)<500){
ctx.moveTo(posx2[i],posy2[i]);
ctx.lineTo(xa, ya);
}
for(var j = 0;j<posx3_len;j++){
if(Math.abs(posx2[i]-posx3[j])<500 && Math.abs(posy2[i]-posy3[j])<500){
ctx.moveTo(posx2[i],posy2[i]);
ctx.lineTo(posx3[j], posy3[j]);
}
}
}
posx2[posx2.length-1]=posx2[0];
posy2[posy2.length-1] = posy2[0];
for(var i = 0;i<posx3_len;i++){
posx3[i] = posx3[i]- (Math.sin(sinang)*i)/1.2;
posy3[i] = posy3[i]-(Math.sin(sinang)*i)/1.2;
if(Math.abs(posx3[i]-xa)<500 && Math.abs(posy3[i]-ya)<500){
ctx.moveTo(posx3[i],posy3[i]);
ctx.lineTo(xa, ya);
}
}
posx3[posx3.length-1]=posx3[0];
posy3[posy3.length-1] = posy3[0];
ctx.restore();
ctx.strokeStyle = 'rgba(255,255,255,'+opa+')';
ctx.stroke();
window.requestAnimationFrame(draw);
t2=performance.now();
console.log(t2-t1);
}
window.requestAnimationFrame(draw);
</script>
</html>

Related

An error in the checkers code, how to fix it?

Made a small game, the main goal is to remove all the pieces by jumping over the checkers. The first time it is possible to remove the checker, the second time it gives an error?
Uncaught TypeError: Cannot read property 'theRow' of undefined
On line 180. I looked at the line, here it is.
Code:
Point = function(x, y) {
this.x = x;
this.y = y;
}
window.onload = function() {
var canvas;
var context;
var BB;
var offsetX;
var offsetY;
var dragok = false;
var startX;
var startY;
var oldX, oldY;
var fieldArray = [
[1, 0, 1, 1, 0, 0],
[1, 0, 0, 1, 1, 0],
[0, 0, 0, 0, 0, 0]
];
var shapes = [];
var possibleLandings = [];
var localX, localY;
var pickedMonster;
var sx = 0;
var sy = 0;
var i1 = 0;
function draw() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.save();
for (var i = 0; i < fieldArray.length; i++) {
for (var j = 0; j < fieldArray[i].length; j++) {
context.lineWidth = 1;
context.strokeRect(j * 60, i * 60, 60, 60);
context.fillRect(j * 60, i * 60, 60, 60);
}
}
for (var i = 0; i < shapes.length; i++) {
circle(shapes[i]);
}
context.restore();
if (possibleLandings.length > 0) {
context.save();
context.fillStyle = "#4CFF00";
context.fillRect(oldX, oldY, 20, 20);
context.restore();
} else {
context.save();
context.fillStyle = "#FF0000";
context.fillRect(oldX, oldY, 20, 20);
context.restore();
}
}
function circle(c) {
context.fillStyle = c.fill;
context.beginPath();
context.arc(c.x, c.y, c.r, 0, Math.PI * 2);
context.closePath();
context.fill();
}
function myDown(e) {
e.preventDefault();
e.stopPropagation();
var mx = parseInt(e.clientX - offsetX);
var my = parseInt(e.clientY - offsetY);
dragok = false;
for (var i = 0; i < shapes.length; i++) {
var s = shapes[i];
var dx = s.x - mx;
var dy = s.y - my;
if (dx * dx + dy * dy < s.r * s.r) {
dragok = true;
s.isDragging = true;
pickedMonster = {
x: s.x,
y: s.y,
i: i,
theRow: s.theRow,
theCol: s.theCol
};
oldX = s.x;
oldY = s.y;
localX = mx - s.x + (30 / 2);
localY = my - s.y + (30 / 2);
checkMonster(s);
canvas.onmousemove = moveMonster;
canvas.onmouseup = dropMonster;
}
}
startX = mx;
startY = my;
}
function moveMonster(e) {
if (dragok) {
e.preventDefault();
e.stopPropagation();
var mx = parseInt(e.clientX - offsetX);
var my = parseInt(e.clientY - offsetY);
var dx = mx - startX;
var dy = my - startY;
for (var i = 0; i < shapes.length; i++) {
var s = shapes[i];
if (s.isDragging) {
s.x += dx;
s.y += dy;
pickedMonster.x = e.clientX - localX;
pickedMonster.y = e.clientY - localY;
}
}
draw();
startX = mx;
startY = my;
}
}
function dropMonster(e) {
e.preventDefault();
e.stopPropagation();
var legalMove = false;
var dropX = Math.floor((pickedMonster.x + 30) / 60);
var dropY = Math.floor((pickedMonster.y + 30) / 60);
console.log(dropX);
for (var i = 0; i < possibleLandings.length; i++) {
if (possibleLandings[i].x == dropY && possibleLandings[i].y == dropX) {
legalMove = true;
break;
}
}
if (!legalMove) {
shapes[pickedMonster.i].x = oldX;
shapes[pickedMonster.i].y = oldY;
} else {
var rowOffset = (dropY - pickedMonster.theRow) / 2;
var colOffset = (dropX - pickedMonster.theCol) / 2;
fieldArray[pickedMonster.theRow][pickedMonster.theCol] = 0;
fieldArray[pickedMonster.theRow + rowOffset][pickedMonster.theCol + colOffset] = 0;
fieldArray[pickedMonster.theRow + 2 * rowOffset][pickedMonster.theCol + 2 * colOffset] = 1;
for (i = 0; i < 5; i++) {
var currentMonster = shapes[i];
if (currentMonster.theRow == pickedMonster.theRow + rowOffset &&
currentMonster.theCol == pickedMonster.theCol + colOffset) {
shapes.splice(i, 1);
}
}
}
dragok = false;
for (var i = 0; i < shapes.length; i++) {
shapes[i].isDragging = false;
}
draw();
}
function checkMonster(s) {
for (var i = 0; i < 4; i++) {
var deltaRow = (1 - i) * (i % 2 - 1);
var deltaCol = (2 - i) * (i % 2);
if (checkField(s, deltaRow, deltaCol)) {
possibleLandings.push(new Point(s.theRow + 2 * deltaRow, s.theCol + 2 * deltaCol));
}
}
}
function checkField(s, rowOffset, colOffset) {
if (fieldArray[s.theRow + 2 * rowOffset] != undefined &&
fieldArray[s.theRow + 2 * rowOffset][s.theCol + 2 * colOffset] != undefined) {
if (fieldArray[s.theRow + rowOffset][s.theCol + colOffset] == 1 &&
fieldArray[s.theRow + 2 * rowOffset][s.theCol + 2 * colOffset] == 0) {
return true;
}
}
return false;
}
function main() {
canvas = document.getElementById("drawingCanvas"),
context = canvas.getContext("2d");
canvas.style.background = "#A0A0A0"
BB = canvas.getBoundingClientRect();
context.fillStyle = 'rgb(150,190,255)';
context.globalAlpha = 0.7;
offsetX = BB.left;
offsetY = BB.top;
for (var i = 0; i < fieldArray.length; i++) {
for (var j = 0; j < fieldArray[i].length; j++) {
if (fieldArray[i][j] == 1) {
shapes.push({
x: j * 60 + 30,
y: i * 60 + 30,
r: 30,
theRow: i,
theCol: j,
fill: "#444444",
isDragging: false
});
}
}
}
draw();
canvas.onmousedown = myDown;
}
main();
}
<canvas id="drawingCanvas" width="500" height="500">
How to fix?
At the end of the game shapes is empty but you are getting shapes[i]. That would return undefined. And that's what the error means undefined doesn't have a property theRow.
You should maybe rerun main() or only rerun this:
for (var i = 0; i < fieldArray.length; i++) {
for (var j = 0; j < fieldArray[i].length; j++) {
if (fieldArray[i][j] == 1) {
shapes.push({
x: j * 60 + 30,
y: i * 60 + 30,
r: 30,
theRow: i,
theCol: j,
fill: "#444444",
isDragging: false
});
}
}
}

Removing mouse controls from Javascript code

I have a large piece of code that draws feather-looking designs controlled by the mouse. This is using mouse pressed and mouseX, mouseY.
I would like the code to run and draw the feathers without the mouse being involved (ie. opening the canvas and having feathers automatically draw on the screen).
The full code is below:
var points = [];
var painting = false;
var strokeNumber = 0;
var scl = 6;
var cols, rows;
var inc = 0.1;
var zOff = 0;
var particles = [];
var flowField = [];
var saturation = [];
function setup() {
createCanvas(windowWidth, windowHeight);
// createCanvas(400, 400);
pixelDensity(5);
background(0);
cols = floor(width / scl);
rows = floor(height / scl);
flowField = Array(cols * rows);
saturation = Array(width * height).fill(0);
greateForceField();
}
function mousePressed() {
painting = true;
strokeNumber++;
}
function mouseReleased() {
painting = false;
}
function updateForceField(){
var v = createVector(mouseX, mouseY);
var vPrev = createVector(pmouseX, pmouseY);
v.sub(vPrev);
v.setMag(1);
var i = floor(mouseX / scl);
var j = floor(mouseY / scl);
var index = i * rows + j;
flowField[index] = v;
}
function showForceField(){
for(var i = 0; i < cols; i++){
for(var j = 0; j < rows; j++){
var index = i * rows + j;
var v = flowField[index];
stroke(0,50);
strokeWeight(1);
push();
translate(i * scl, j * scl);
rotate(v.heading());
line(0,0,scl,0);
pop();
}
}
}
function greateForceField(){
var xOff = 0;
for(var i = 0; i < cols; i++){
var yOff = 0;
for(var j = 0; j < rows; j++){
yOff += inc;
var angle = noise(xOff, yOff, zOff) * TWO_PI;
var v = p5.Vector.fromAngle(angle);
v.setMag(.1);
var index = i * rows + j;
flowField[index] = v;
}
xOff += inc;
}
// zOff += inc * 0.1;
}
function draw() {
// background(255);
// showForceField();
if(painting){
updateForceField();
var idx = mouseY * width + mouseX;
if(saturation[idx] < 10){
var r = 1+sqrt(sq(mouseX-pmouseX)+sq(mouseY-pmouseY));
for(var a = 0; a < 100; a++){
var particle = new Particle(mouseX+random()*r*cos(random(TWO_PI)), mouseY+random()*r*sin(random(TWO_PI)));
particles.push(particle);
}
saturation[idx] ++;
}
}
particles.filter(particle => particle.spread > 0).map(particle => {
particle.update();
particle.show();
// particle.edges();
particle.follow();
})
particles.map((particle, idx) => {
if(particle.spread <= 0){
particles.splice(idx,1);
}
});
}
function Particle(x,y){
this.pos = createVector(x,y);
// this.color = color(245, 225, 50);
// this.color = color(145, 225, 192);
this.color = color(255);
this.spread = 127;
this.spreadInc = this.spread/100;
this.prevPos = this.pos.copy();
this.vel = p5.Vector.random2D();
this.acc = createVector(0,0);
this.maxSpeed = 2;
this.update = function(){
this.spread -= this.spreadInc;
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.mult(0);
}
this.applyForce = function(force){
this.acc.add(force);
}
this.follow = function(){
var i = floor(this.pos.x / scl);
var j = floor(this.pos.y / scl);
var index = i * rows + j;
var force = flowField[index];
this.applyForce(force);
}
this.show = function(){
stroke(red(this.color),green(this.color),blue(this.color),this.spread);
strokeWeight(.3*this.spread/127);
// point(this.pos.x, this.pos.y);
line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
this.updatePrev();
}
this.updatePrev = function(){
this.prevPos = this.pos.copy();
}
this.edges = function(){
if(this.pos.x > width) {
this.pos.x = 0;
this.updatePrev();
}
if(this.pos.x < 0){
this.pos.x = width;
this.updatePrev();
}
if(this.pos.y > height){
this.pos.y = 0;
this.updatePrev();
}
if(this.pos.y < 0) {
this.pos.y = height;
this.updatePrev();
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>
Use 4 variables current_x, current_y, prev_x and prev_y instead of mosueX, mouseY, pmouseX and pmouseY.
var current_x=0, current_y=0, prev_x=0, prev_y=0;
function updateForceField(){
var v = createVector(current_x, current_y);
var vPrev = createVector(prev_x, prev_y);
v.sub(vPrev);
v.setMag(1);
var i = floor(current_x / scl);
var j = floor(current_y / scl);
var index = i * rows + j;
flowField[index] = v;
}
Instead of the state painting use a time interval. The time between 2 frames can be get from the built-in variable deltaTime (in milliseconds). Set the x and y positions by random values. e.g.:
var interval = 200; // 200 milliseconds
var sum_time = 0;
function draw() {
sum_time += deltaTime;
if(sum_time > interval){
sum_time = 0;
current_x = Math.floor(random(width));
current_y = Math.floor(random(height));
updateForceField();
prev_x = current_x;
prev_y = current_y;
var idx = current_y * width + current_x;
if(saturation[idx] < 10){
var r = 1+sqrt(sq(current_x-prev_x)+sq(current_y-prev_y));
for(var a = 0; a < 100; a++){
var particle = new Particle(current_x+random()*r*cos(random(TWO_PI)), current_y+random()*r*sin(random(TWO_PI)));
particles.push(particle);
}
saturation[idx] ++;
}
}
// [...]
}
See the example:
var points = [];
var painting = false;
var strokeNumber = 0;
var scl = 6;
var cols, rows;
var inc = 0.1;
var zOff = 0;
var particles = [];
var flowField = [];
var saturation = [];
function setup() {
//createCanvas(windowWidth, windowHeight);
createCanvas(500, 200);
pixelDensity(5);
background(0);
cols = floor(width / scl);
rows = floor(height / scl);
flowField = Array(cols * rows);
saturation = Array(width * height).fill(0);
greateForceField();
}
function mousePressed() {
painting = true;
strokeNumber++;
}
function mouseReleased() {
painting = false;
}
var current_x=0, current_y=0, prev_x=0, prev_y=0;
function updateForceField(){
var v = createVector(current_x, current_y);
var vPrev = createVector(prev_x, prev_y);
v.sub(vPrev);
v.setMag(1);
var i = floor(current_x / scl);
var j = floor(current_y / scl);
var index = i * rows + j;
flowField[index] = v;
}
function showForceField(){
for(var i = 0; i < cols; i++){
for(var j = 0; j < rows; j++){
var index = i * rows + j;
var v = flowField[index];
stroke(0,50);
strokeWeight(1);
push();
translate(i * scl, j * scl);
rotate(v.heading());
line(0,0,scl,0);
pop();
}
}
}
function greateForceField(){
var xOff = 0;
for(var i = 0; i < cols; i++){
var yOff = 0;
for(var j = 0; j < rows; j++){
yOff += inc;
var angle = noise(xOff, yOff, zOff) * TWO_PI;
var v = p5.Vector.fromAngle(angle);
v.setMag(.1);
var index = i * rows + j;
flowField[index] = v;
}
xOff += inc;
}
// zOff += inc * 0.1;
}
var interval = 200; // 200 milliseconds
var sum_time = 0;
function draw() {
// background(255);
// showForceField();
sum_time += deltaTime;
if(sum_time > interval){
sum_time = 0;
current_x = Math.floor(random(width));
current_y = Math.floor(random(height));
updateForceField();
prev_x = current_x;
prev_y = current_y;
var idx = current_y * width + current_x;
if(saturation[idx] < 10){
var r = 1+sqrt(sq(current_x-prev_x)+sq(current_y-prev_y));
for(var a = 0; a < 100; a++){
var particle = new Particle(current_x+random()*r*cos(random(TWO_PI)), current_y+random()*r*sin(random(TWO_PI)));
particles.push(particle);
}
saturation[idx] ++;
}
}
particles.filter(particle => particle.spread > 0).map(particle => {
particle.update();
particle.show();
// particle.edges();
particle.follow();
})
particles.map((particle, idx) => {
if(particle.spread <= 0){
particles.splice(idx,1);
}
});
}
function Particle(x,y){
this.pos = createVector(x,y);
// this.color = color(245, 225, 50);
// this.color = color(145, 225, 192);
this.color = color(255);
this.spread = 127;
this.spreadInc = this.spread/100;
this.prevPos = this.pos.copy();
this.vel = p5.Vector.random2D();
this.acc = createVector(0,0);
this.maxSpeed = 2;
this.update = function(){
this.spread -= this.spreadInc;
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.mult(0);
}
this.applyForce = function(force){
this.acc.add(force);
}
this.follow = function(){
var i = floor(this.pos.x / scl);
var j = floor(this.pos.y / scl);
var index = i * rows + j;
var force = flowField[index];
this.applyForce(force);
}
this.show = function(){
stroke(red(this.color),green(this.color),blue(this.color),this.spread);
strokeWeight(.3*this.spread/127);
// point(this.pos.x, this.pos.y);
line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
this.updatePrev();
}
this.updatePrev = function(){
this.prevPos = this.pos.copy();
}
this.edges = function(){
if(this.pos.x > width) {
this.pos.x = 0;
this.updatePrev();
}
if(this.pos.x < 0){
this.pos.x = width;
this.updatePrev();
}
if(this.pos.y > height){
this.pos.y = 0;
this.updatePrev();
}
if(this.pos.y < 0) {
this.pos.y = height;
this.updatePrev();
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>

Fix particle system errors in processing

I'm trying to set up a feather-looking particle system in processing. I am trying to base it off of some code I found on OpenProcessing. When I copy and paste the code into processing (using Java) I get an error saying "expecting SEMI, found 'points'.
I figured this was maybe because the code uses var and not int indicating that this would be Javascript code instead. So I switched the processing mode to p5.js, and it runs but the browser that opens is just a blank white screen.
Any help on getting this to run would be appreciated! Thanks!
The code is below:
var points = [];
var painting = false;
var strokeNumber = 0;
var scl = 6;
var cols, rows;
var inc = 0.1;
var zOff = 0;
var particles = [];
var flowField = [];
var saturation = [];
function setup() {
createCanvas(windowWidth, windowHeight);
// createCanvas(400, 400);
background(0);
pixelDensity(5);
cols = floor(width / scl);
rows = floor(height / scl);
flowField = Array(cols * rows);
saturation = Array(width * height).fill(0);
greateForceField();
}
function mousePressed() {
painting = true;
strokeNumber++;
}
function mouseReleased() {
painting = false;
}
function updateForceField(){
var v = createVector(mouseX, mouseY);
var vPrev = createVector(pmouseX, pmouseY);
v.sub(vPrev);
v.setMag(1);
var i = floor(mouseX / scl);
var j = floor(mouseY / scl);
var index = i * rows + j;
flowField[index] = v;
}
function showForceField(){
for(var i = 0; i < cols; i++){
for(var j = 0; j < rows; j++){
var index = i * rows + j;
var v = flowField[index];
stroke(0,50);
strokeWeight(1);
push();
translate(i * scl, j * scl);
rotate(v.heading());
line(0,0,scl,0);
pop();
}
}
}
function greateForceField(){
var xOff = 0;
for(var i = 0; i < cols; i++){
var yOff = 0;
for(var j = 0; j < rows; j++){
yOff += inc;
var angle = noise(xOff, yOff, zOff) * TWO_PI;
var v = p5.Vector.fromAngle(angle);
v.setMag(.1);
var index = i * rows + j;
flowField[index] = v;
}
xOff += inc;
}
// zOff += inc * 0.1;
}
function draw() {
// background(255);
// showForceField();
if(painting){
updateForceField();
var idx = mouseY * width + mouseX;
if(saturation[idx] < 10){
var r = 1+sqrt(sq(mouseX-pmouseX)+sq(mouseY-pmouseY));
for(var a = 0; a < 100; a++){
var particle = new Particle(mouseX+random()*r*cos(random(TWO_PI)), mouseY+random()*r*sin(random(TWO_PI)));
particles.push(particle);
}
saturation[idx] ++;
}
}
particles.filter(particle => particle.spread > 0).map(particle => {
particle.update();
particle.show();
// particle.edges();
particle.follow();
})
particles.map((particle, idx) => {
if(particle.spread <= 0){
particles.splice(idx,1);
}
});
}
function Particle(x,y){
this.pos = createVector(x,y);
// this.color = color(245, 225, 50);
// this.color = color(145, 225, 192);
this.color = color(255);
this.spread = 127;
this.spreadInc = this.spread/100;
this.prevPos = this.pos.copy();
this.vel = p5.Vector.random2D();
this.acc = createVector(0,0);
this.maxSpeed = 2;
this.update = function(){
this.spread -= this.spreadInc;
this.vel.add(this.acc);
this.vel.limit(this.maxSpeed);
this.pos.add(this.vel);
this.acc.mult(0);
}
this.applyForce = function(force){
this.acc.add(force);
}
this.follow = function(){
var i = floor(this.pos.x / scl);
var j = floor(this.pos.y / scl);
var index = i * rows + j;
var force = flowField[index];
this.applyForce(force);
}
this.show = function(){
stroke(red(this.color),green(this.color),blue(this.color),this.spread);
strokeWeight(.3*this.spread/127);
// point(this.pos.x, this.pos.y);
line(this.pos.x, this.pos.y, this.prevPos.x, this.prevPos.y);
this.updatePrev();
}
this.updatePrev = function(){
this.prevPos = this.pos.copy();
}
this.edges = function(){
if(this.pos.x > width) {
this.pos.x = 0;
this.updatePrev();
}
if(this.pos.x < 0){
this.pos.x = width;
this.updatePrev();
}
if(this.pos.y > height){
this.pos.y = 0;
this.updatePrev();
}
if(this.pos.y < 0) {
this.pos.y = height;
this.updatePrev();
}
}
}
This looks like javascript waaay more than java. I'm not exactly a buff in these matters, but... are you trying to run javascript as java?
If you are using the Processing IDE, look in the upper right corner. Do you see the word "Java" ?
Like this:
If this is the case, you might want to consider installing p5.js :
Click here and choose "Add mode":
Now search for p5.js and install it:
Now your code will compile. I'm not saying it'll work, though, but your current problem will be behind you. Have fun!

Codehs and KhanAcademy different use of JavaScript?

At my school I am learning how to code in JS using a site called codehs.com. After a while I learned about graphics with JS. There was this one point where I had to create a circle:
var circle = new Circle(50);
circle.setPosition(100,100);
add(circle);
After a few days I came across another website that was teaching students how code using JS. The website was called khanacademy.org I was interested and saw that the first lesson was making drawings. I looked at the video provided and it had a different code to make a circle.
ellipse(203, 197, 300, 350);
I am confused on how to make a circle using JS since I just started.
I'm one of the founders of CodeHS. CodeHS uses a custom JavaScript library on top of regular JavaScript. Khan Academy uses Processing JS, which is a different library (You can use Processing on CodeHS as well if you like).
You can see the documentation for everything in the CodeHS JS library at https://codehs.com/docs and learn how to use it in the Intro CS in JavaScript course.
We have designed this library to be great for learning -- it gives you experience using Object Oriented Programming while making it simple to create and manipulate shapes for programs like a Helicopter Game.
Additionally, you can include the library on an HTML page that runs JavaScript by adding this script tag to your page.
<script type="text/javascript" src="https://static.codehs.com/gulp/3d065bc81d3b7edf21e928ce2c764374a03c5cd6/chs-js-lib/chs.js"></script>
Here's an example of a full HTML page that runs JavaScript and uses the CodeHS library on it to draw a circle.
<html>
<head>
<title>Circle Example</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript" src="https://static.codehs.com/gulp/3d065bc81d3b7edf21e928ce2c764374a03c5cd6/chs-js-lib/chs.js"></script>
<style>
canvas {
border: 1px solid black;
display: inline-block;
vertical-align: top;
}
pre {
border: 1px solid black;
display: inline-block;
width: 400px;
height: 500px;
background-color: #F5F5F5;
}
</style>
</head>
<body>
<h1>Circle Example</h1>
<canvas
width="400"
height="500"
class="codehs-editor-canvas"></canvas>
<script>
window.onload = function() {
var circle = new Circle(50);
circle.setPosition(100,100);
add(circle);
};
</script>
</body>
</html>
Looks like KHAN ACADEMY uses ProcessingJS to draw the circle
I was unable to check what is the library CodeHS uses to draw a circle, but has to be a different one. But the fact is that there are so many good libraries developed in javascript to make whatever you can imagine. They're generally different one from another but their goal is to make our life easier.
JavaScript library | Wikipedia
What's a JS library? | KHAN ACADEMY
I tried using the processing platform for CodeHs, just copying and pasting this code:
/**
* This program finds the shortest path through a series of obstacles using A* search,
* smooths the path, then uses PID control to drive a robot to the goal.
**/
// You can play around with these constants---
var NUM_BLOCKS = 20;
var OBSTACLE_PROBABILITY = 0.2;
var MOVE_NOISE = 0.1;
var STEERING_NOISE = 0.1;
var ROBOT_SPEED = 0.5;
var MAX_STEERING_ANGLE = Math.PI/4.0;
var tP = 2.0;
var tI = 0.0001;
var tD = 16.0;
var weightData = 0.1;
var weightSmooth = 0.1;
// Search types
var A_STAR = 0;
var GREEDY = 1;
var BREADTH_FIRST = 2;
var DEPTH_FIRST = 3;
var SEARCH_MODE = A_STAR;
// --------------------------------------------
var BLOCK_SIZE = width/NUM_BLOCKS;
var START = 2;
var GOAL_X = NUM_BLOCKS-1;
var GOAL_Y = NUM_BLOCKS-1;
var GOAL = 3;
var START_COLOR = color(23, 33, 176);
var GOAL_COLOR = color(199, 188, 68);
var FRONTIER_COLOR = color(105, 179, 105);
var EXPLORED_COLOR = color(117, 117, 117, 100);
var PATH_COLOR = color(166, 53, 53);
var SMOOTH_PATH_COLOR = color(53, 68, 166);
var PLAN = 0;
var SMOOTH = 1;
var CALIBRATE = 2;
var NAVIGATE = 3;
var DELTA = [[-1, 0], [1, 0], [0, -1], [0, 1]];
var frontier = [[0, 0, 0, 0]];
var explored = [];
var predecessors = [];
var path = [];
var smoothPath = [];
var mode = PLAN;
var index = 0;
var cte = 0;
var sigma = 0;
angleMode = "radians";
frameRate(60);
// Initialize world
var world = [];
for (var i = 0; i < NUM_BLOCKS; i++) {
var row = [];
for (var j = 0; j < NUM_BLOCKS; j++) {
var r = random();
if (r < OBSTACLE_PROBABILITY) {
row.push(1);
}
else {
row.push(0);
}
}
world.push(row);
}
world[0][0] = START;
world[GOAL_Y][GOAL_X] = GOAL;
for (var i = 0; i < NUM_BLOCKS; i++) {
var row = [];
for (var j = 0; j < NUM_BLOCKS; j++) {
row.push(false);
}
explored.push(row);
}
explored[0][0] = true;
for (var i = 0; i < NUM_BLOCKS; i++) {
var row = [];
for (var j = 0; j < NUM_BLOCKS; j++) {
row.push(null);
}
predecessors.push(row);
}
var Robot = function(x, y, size) {
this.x = x;
this.y = y;
this.vx = 0;
this.vy = 0;
this.orientation = 0;
this.size = size;
// Thanks to Sebastian Thrun for this code:
// https://www.udacity.com/course/viewer#!/c-cs373/l-48696626/e-48403941/m-48729137
// My code for this movement can be found at:
// https://www.khanacademy.org/computer-programming/bicycle-model/5496951953031168
this.move = function(d, theta) {
var generator = new Random(millis());
var moveDistance = (generator.nextGaussian() * MOVE_NOISE) + d;
var turnAngle = (generator.nextGaussian() * STEERING_NOISE) + theta;
turnAngle = min(turnAngle, MAX_STEERING_ANGLE);
turnAngle = max(turnAngle, -MAX_STEERING_ANGLE);
var turn = tan(turnAngle)*moveDistance/this.size;
// Approximately straight motion
if (abs(turn) < 0.001) {
this.x += moveDistance*cos(this.orientation);
this.y += moveDistance*sin(this.orientation);
this.orientation = (this.orientation+turn)%(2.0*Math.PI);
}
// Move using the bicyle model
else {
var radius = moveDistance/turn;
var cx = this.x-(sin(this.orientation)*radius);
var cy = this.y+(cos(this.orientation)*radius);
this.orientation = (this.orientation+turn)%(2.0*Math.PI);
this.x = cx + (sin(this.orientation)*radius);
this.y = cy - (cos(this.orientation)*radius);
}
};
this.draw = function() {
pushMatrix();
translate(this.x, this.y);
rotate(this.orientation);
fill(128, 27, 27);
stroke(255, 0, 0);
rect(-this.size/2, -(this.size*0.75*0.5), this.size, this.size*0.75);
popMatrix();
};
};
var robot;
var addToFrontier = function(node) {
// Insert the node into the frontier
// Order by lowest cost
var i = frontier.length-1;
if (SEARCH_MODE === A_STAR) {
while (i > 0 && node[2]+node[3] < frontier[i][2]+frontier[i][3]) {
i--;
}
}
else if (SEARCH_MODE === GREEDY) {
while (i > 0 && node[3] < frontier[i][3]) {
i--;
}
}
else if (SEARCH_MODE === BREADTH_FIRST) {
frontier.push(node);
}
else if (SEARCH_MODE === DEPTH_FIRST) {
frontier.splice(0, 0, node);
}
frontier.splice(i+1, 0, node);
};
var distance = function(x1, y1, x2, y2) {
return sqrt(pow(x1-x2, 2) + pow(y1-y2, 2));
};
var manhattanDistance = function(x1, y1, x2, y2) {
return abs(x1-x2) + abs(y1-y2);
};
var drawWorld = function() {
background(255, 255, 255);
for (var i = 0; i < world.length; i++) {
for (var j = 0; j < world[0].length; j++) {
if (world[i][j] === 1) {
stroke(0, 0, 0);
fill(0, 0, 0);
}
else if (world[i][j] === START) {
fill(START_COLOR);
stroke(START_COLOR);
}
else if (world[i][j] === GOAL) {
fill(GOAL_COLOR);
stroke(GOAL_COLOR);
}
else {
fill(255, 255, 255);
noStroke();
}
rect(j*BLOCK_SIZE, i*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
}
};
var simulate = function(steps) {
var error = 0;
var crosstrackError = 0;
var r = new Robot(0, 1, BLOCK_SIZE/2);
var sigma = 0;
var diff = 0;
for (var i = 0; i < steps*2; i++) {
// Compute cte
diff = r.y-crosstrackError;
crosstrackError = r.y;
sigma += crosstrackError;
if (i > steps) {
error += pow(crosstrackError, 2);
}
// Update robot
r.move(ROBOT_SPEED, -tP*crosstrackError - tD*diff - tI*sigma);
}
return error;
};
draw = function() {
drawWorld();
// Use A* to find a path to the goal
if (mode === PLAN) {
// Draw explored
fill(EXPLORED_COLOR);
noStroke();
for (var i = 0; i < explored.length; i++) {
for (var j = 0; j < explored[0].length; j++) {
if (explored[i][j]) {
rect(j*BLOCK_SIZE, i*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
}
}
// Draw frontier
fill(FRONTIER_COLOR);
noStroke();
for (var i = 0; i < frontier.length; i++) {
rect(frontier[i][0]*BLOCK_SIZE, frontier[i][1]*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE);
}
// A*
if (frontier.length > 0) {
// Remove a node from the frontier
var x = frontier[0][0];
var y = frontier[0][1];
var cost = frontier[0][2];
//println(cost + ", " + frontier[0][3]);
frontier.splice(0, 1);
// Goal check
if (world[y][x] === GOAL) {
mode = SMOOTH;
}
else {
// Add all adjacent unexplored nodes
for (var i = 0; i < DELTA.length; i++) {
// If the new position is in the world
var x2 = x + DELTA[i][0];
var y2 = y + DELTA[i][1];
if (x2 >= 0 && x2 < world[0].length && y2 >= 0 && y2 < world.length) {
// If the position is unexplored
if (!explored[y2][x2] && world[y2][x2] !== 1) {
explored[y2][x2] = true;
predecessors[y2][x2] = [x, y];
addToFrontier([x2, y2, cost+1, dist(x2, y2, GOAL_X, GOAL_Y)]);
}
}
}
}
}
else {
mode = -1;
println("No possible path to goal.");
}
}
// Smooth the path
else if (mode === SMOOTH) {
// Build path
if (path.length === 0) {
var x = GOAL_X;
var y = GOAL_Y;
path.splice(0, 0, [x, y]);
smoothPath.splice(0, 0, [x, y]);
while (x !== 0 || y !== 0) {
var newX = predecessors[y][x][0];
var newY = predecessors[y][x][1];
x = newX;
y = newY;
path.splice(0, 0, [x, y]);
smoothPath.splice(0, 0, [x, y]);
}
}
// Draw the original path
stroke(PATH_COLOR);
strokeWeight(5);
for (var i = 0; i < path.length; i++) {
point(BLOCK_SIZE*path[i][0]+BLOCK_SIZE/2, BLOCK_SIZE*path[i][1]+BLOCK_SIZE/2);
}
strokeWeight(1);
for (var i = 0; i < path.length-1; i++) {
line(BLOCK_SIZE*path[i][0]+BLOCK_SIZE/2, BLOCK_SIZE*path[i][1]+BLOCK_SIZE/2, BLOCK_SIZE*path[i+1][0]+BLOCK_SIZE/2, BLOCK_SIZE*path[i+1][1]+BLOCK_SIZE/2);
}
// Draw the new path
stroke(SMOOTH_PATH_COLOR);
strokeWeight(5);
for (var i = 0; i < smoothPath.length; i++) {
point(BLOCK_SIZE*smoothPath[i][0]+BLOCK_SIZE/2, BLOCK_SIZE*smoothPath[i][1]+BLOCK_SIZE/2);
}
strokeWeight(1);
for (var i = 0; i < smoothPath.length-1; i++) {
line(BLOCK_SIZE*smoothPath[i][0]+BLOCK_SIZE/2, BLOCK_SIZE*smoothPath[i][1]+BLOCK_SIZE/2, BLOCK_SIZE*smoothPath[i+1][0]+BLOCK_SIZE/2,
BLOCK_SIZE*smoothPath[i+1][1]+BLOCK_SIZE/2);
}
// Perform gradient descent
var update;
var diff = 0;
for (var i = 1; i < smoothPath.length-1; i++) {
update = [0, 0];
for (var j = 0; j < smoothPath[0].length; j++) {
update[j] += weightData * (path[i][j] - smoothPath[i][j]);
update[j] += weightSmooth * (smoothPath[(i+1)%smoothPath.length][j] + smoothPath[(i-1+smoothPath.length)%smoothPath.length][j] - 2*smoothPath[i][j]);
}
// Simulataneous update
for (var j = 0; j < smoothPath[0].length; j++) {
smoothPath[i][j] += update[j];
diff += abs(update[j]);
}
}
if (diff < 0.000001) {
robot = new Robot(BLOCK_SIZE/2, BLOCK_SIZE/2, BLOCK_SIZE/2);
mode = NAVIGATE;
}
}
else if (mode === CALIBRATE) {
var steps = 100;
var error = simulate(steps);
var dp = [1.0, 1.0, 1.0];
var params = [0, 0, 0];
if (error/steps > 0.04) {
for (var i = 0; i < dp.length; i++) {
params[i] += dp[i];
var newError = simulate(steps);
if (newError < error) {
error = newError;
dp[i] *= 1.1;
}
else {
params[i] -= 2*dp[i];
newError = simulate(steps);
if (newError < error) {
error = newError;
dp[i] *= -1.1;
}
else {
params[i] += dp[i];
dp[i] *= 0.9;
}
}
}
tP = params[0];
tD = params[1];
tI = params[2];
println(error/steps);
}
else {
println(params);
tP = params[0];
tD = params[1];
tI = params[2];
mode = NAVIGATE;
}
}
// Use PID control to follow the path
else if (mode === NAVIGATE) {
// Draw path
stroke(SMOOTH_PATH_COLOR);
strokeWeight(5);
for (var i = 0; i < smoothPath.length; i++) {
point(BLOCK_SIZE*smoothPath[i][0]+BLOCK_SIZE/2, BLOCK_SIZE*smoothPath[i][1]+BLOCK_SIZE/2);
}
strokeWeight(1);
for (var i = 0; i < smoothPath.length-1; i++) {
line(BLOCK_SIZE*smoothPath[i][0]+BLOCK_SIZE/2, BLOCK_SIZE*smoothPath[i][1]+BLOCK_SIZE/2, BLOCK_SIZE*smoothPath[i+1][0]+BLOCK_SIZE/2,
BLOCK_SIZE*smoothPath[i+1][1]+BLOCK_SIZE/2);
}
// Draw robot
robot.draw();
// Compute cte
var diff = -cte;
var x1 = smoothPath[index][0]*BLOCK_SIZE+BLOCK_SIZE/2;
var y1 = smoothPath[index][1]*BLOCK_SIZE+BLOCK_SIZE/2;
var x2 = smoothPath[index+1][0]*BLOCK_SIZE+BLOCK_SIZE/2;
var y2 = smoothPath[index+1][1]*BLOCK_SIZE+BLOCK_SIZE/2;
var dx = x2-x1;
var dy = y2-y1;
var d = sqrt(pow(dx, 2) + pow(dy, 2));
var u = ((robot.x-x1)*dx + (robot.y-y1)*dy)/(pow(dx, 2) + pow(dy, 2));
var Px = x1 + d*u*cos(atan2(dy, dx));
var Py = y1 + d*u*sin(atan2(dy, dx));
cte = ((robot.y-y1)*dx-(robot.x-x1)*dy)/(pow(dx, 2) + pow(dy, 2));
sigma += cte;
diff += cte;
if (u > 1) {
index++;
index = min(index, smoothPath.length-2);
}
// Update robot
robot.move(ROBOT_SPEED, -tP*cte - tD*diff - tI*sigma);
//println(index);
}
};
but all that happens is that a grey screen shows up

How to stop line colouring over transparent canvas?

I need some expert help. When I make canvas background transparent the line colors the whole canvas (shown in code below).
How do I stop/fix this? I want the line stay as a single line that doesn't color the canvas.
Math.clamp = function(x, min, max) {
return x < min ? min : (x > max ? max : x);
};
// canvas settings
var viewWidth = window.innerWidth,
viewHeight = window.innerHeight,
drawingCanvas = document.getElementById("drawing_canvas"),
ctx,
timeStep = (10 / 100),
time = 0;
var lineTension = 0.067,
lineDamping = 0.068,
waveSpreadFactor = 0.1;
var previousMousePosition = {
x: 0,
y: 0
},
currentMousePosition = {
x: 0,
y: 0
},
actualMousePosition = {
x: 0,
y: 0
};
var line,
lineSegmentCount = 64,
lineMaxForce = 32,
lineStrokeGradient;
var audioCtx,
nodeCount = 64,
oscillatorNodes = [],
gainNodes = [];
var segmentsPerNode = lineSegmentCount / nodeCount;
function initGui() {
}
function goBananas() {
lineTension = 0.2;
line.anchors[Math.floor(Math.random() * line.anchors.length)].
vel = lineMaxForce;
}
function resetLine() {
line.reset();
for (var i = 0; i < nodeCount; i++) {
oscillatorNodes[i].detune.value = 100;
gainNodes[i].gain.value = 0;
}
lineTension = 0.0025;
lineDamping = 0.05;
waveSpreadFactor = 0.1;
}
function initDrawingCanvas() {
drawingCanvas.width = viewWidth;
drawingCanvas.height = viewHeight;
window.addEventListener('resize', resizeHandler);
window.addEventListener('mousemove', mouseMoveHandler);
setInterval(updateMousePosition, (1000 / 30));
ctx = drawingCanvas.getContext('2d');
ctx.lineWidth = 5;
line = new Line(0, viewHeight * 0.5, viewWidth, lineSegmentCount);
// line.anchors[0].vel = viewHeight * 0.25;
lineStrokeGradient = ctx.createLinearGradient(0, 0, 0, viewHeight);
lineStrokeGradient.addColorStop(0, '#0ff');
}
function initWebAudio() {
audioCtx = new(window.AudioContext || window.webkitAudioContext)();
for (var i = 0; i < nodeCount; i++) {
var oscillatorNode = audioCtx.createOscillator();
var gainNode = audioCtx.createGain();
oscillatorNode.connect(gainNode);
gainNode.connect(audioCtx.destination);
gainNode.gain.value = 0;
oscillatorNode.type = 'saw';
oscillatorNode.detune.value = 200;
oscillatorNode.frequency.value = 1200 * (i / 60);
oscillatorNode.start();
oscillatorNodes[i] = oscillatorNode;
gainNodes[i] = gainNode;
}
}
function resizeHandler() {
drawingCanvas.width = viewWidth = window.innerWidth;
drawingCanvas.height = viewHeight = window.innerHeight;
if (ctx) {
ctx.lineWidth = 4;
line.resize(viewWidth, viewHeight * 0.5);
}
}
function mouseMoveHandler(event) {
actualMousePosition.x = Math.floor(event.clientX);
actualMousePosition.y = Math.floor(event.clientY);
}
function updateMousePosition() {
previousMousePosition.x = currentMousePosition.x;
previousMousePosition.y = currentMousePosition.y;
currentMousePosition.x = actualMousePosition.x;
currentMousePosition.y = actualMousePosition.y;
}
function update() {
var px = Math.min(previousMousePosition.x, currentMousePosition.x),
py = Math.min(previousMousePosition.y, currentMousePosition.y),
pw = Math.max(1, Math.abs(previousMousePosition.x - currentMousePosition.x)),
ph = Math.max(1, Math.abs(previousMousePosition.y - currentMousePosition.y)),
force = Math.clamp(currentMousePosition.y -
previousMousePosition.y, -lineMaxForce, lineMaxForce);
var pixels = ctx.getImageData(px, py, pw, ph),
data = pixels.data;
for (var i = 0; i < data.length; i += 3) {
var r = data[i + 0],
g = data[i + 1],
b = data[i + 2],
x = (i % ph) + px;
if (r + g + b > 0) {
line.ripple(x, force);
}
}
line.update();
for (var j = 0; j < gainNodes.length; j++) {
var anchor = line.anchors[j * segmentsPerNode],
gain = Math.clamp(Math.abs(anchor.vel) / viewHeight * 0.5, 0, 3),
detune = Math.clamp(anchor.pos / viewHeight * 100, 0, 300);
gainNodes[j].gain.value = gain;
oscillatorNodes[j].detune.value = detune;
}
}
function draw() {
ctx.strokeStyle = lineStrokeGradient;
line.draw();
}
window.onload = function() {
initDrawingCanvas();
initWebAudio();
initGui();
requestAnimationFrame(loop);
};
function loop() {
update();
draw();
time += timeStep;
requestAnimationFrame(loop);
}
Line = function(x, y, length, segments) {
this.x = x;
this.y = y;
this.length = length;
this.segments = segments;
this.segmentLength = this.length / this.segments;
this.anchors = [];
for (var i = 0; i <= this.segments; i++) {
this.anchors[i] = {
target: this.y,
pos: this.y,
vel: 0,
update: function() {
var dy = this.pos - this.target,
acc = -lineTension * dy - lineDamping * this.vel;
this.pos += this.vel;
this.vel += acc;
},
reset: function() {
this.pos = this.target;
this.vel = 0;
}
};
}
};
Line.prototype = {
resize: function(length, targetY) {
this.length = length;
this.segmentLength = this.length / this.segments;
for (var i = 0; i <= this.segments; i++) {
this.anchors[i].target = targetY;
}
},
reset: function() {
for (var i = 0; i <= this.segments; i++) {
this.anchors[i].reset();
}
},
ripple: function(origin, amplitude) {
var i = Math.floor((origin - this.x) / this.segmentLength);
if (i >= 0 && i <= this.segments) {
this.anchors[i].vel = amplitude;
}
},
update: function() {
var lDeltas = [],
rDeltas = [],
i;
for (i = 0; i <= this.segments; i++) {
this.anchors[i].update();
}
for (i = 0; i <= this.segments; i++) {
if (i > 0) {
lDeltas[i] = waveSpreadFactor * (this.anchors[i].pos - this.anchors[i - 1].pos);
this.anchors[i - 1].vel += lDeltas[i];
}
if (i < this.segments) {
rDeltas[i] = waveSpreadFactor * (this.anchors[i].pos - this.anchors[i + 1].pos);
this.anchors[i + 1].vel += rDeltas[i];
}
}
for (i = 0; i <= this.segments; i++) {
if (i > 0) {
this.anchors[i - 1].pos += lDeltas[i];
}
if (i < this.segments) {
this.anchors[i + 1].pos += rDeltas[i];
}
}
},
draw: function() {
ctx.beginPath();
for (var i = 0; i <= this.segments; i++) {
ctx.lineTo(
this.x + this.segmentLength * i,
this.anchors[i].pos
);
}
ctx.stroke();
}
};
From the code you posted, the problem seems to be a missing
ctx.clearRect(0, 0, viewWidth, viewHeight)
at the beginning of your "draw" function.
See a working example here

Categories