Rendering too many points on Javascript-player - javascript

As part of a project, I have to render a video on a JS-player from a text file which has the points - all the changed coordinates along-with the color in each frame. Below is the code I'm using to draw these point on the screen.
But the issue is that the number of changed pixels in each frame are as high as ~20,000 and I need to display these in less than 30ms (inter-frame time difference). So, when I run this code the browser hangs for almost each frame. Could someone suggest an improvement for this?
Any help is really appreciated.
c.drawImage(img,0,0,800,800);
setInterval(
function(){
while(tArr[index]==time) {
var my_imageData = c.getImageData(0,0,width, height);
color(my_imageData,Math.round(yArr[index]),Math.round(xArr[index]),Math.round(iArr[index]),255);
c.putImageData(my_imageData,0,0);
index=index+1;
}
time = tArr[index];
}
,30);
xArr, yArr, iArr, tArr are arrays of x-coordinate, y-coordinate, intensity value and time of appearance respectively for the corresponding point to be rendered
function color(imageData,x,y,i,a){ //wrapper function to color the point
var index = (x + y * imageData.width) * 4;
imageData.data[index+0] = i;
imageData.data[index+1] = i;
imageData.data[index+2] = i;
imageData.data[index+3] = a;
}

Related

How do I generate a random X value for each "projectile" in my falling objects game using Javascript?

I am coding a game that is currently in its very early stages for a project to try to learn more about coding. In my game, objects generate randomly (green squares), and the player (red square), avoids them. I am having trouble trying to get the green squares to generate from a random position on the x-axis. I already have a formula to generate a random number for X, but after it selects a number randomly, all the "projectiles" generate there, rather than all generating from a different area. How would I get all the "projectiles" to generate from different positions on the x-axis randomly?
var randomX = Math.floor(Math.random() * 480) + 15;
function updateGameArea() {
var x, y;
for (i = 0; i < projectiles.length; i += 1) {
if (player.crashWith(projectiles[i])) {
gameArea.stop();
return;
}
}
gameArea.clear();
gameArea.frameNo += 1;
if (gameArea.frameNo == 1 || everyinterval(150)) {
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
for (i = 0; i < projectiles.length; i += 1) {
projectiles[i].y += -1; // the shape is using its coordinates to build downward from its y position
projectiles[i].update();
}
player.newPos();
player.update();
}
function everyinterval(n) {
if ((gameArea.frameNo / n) % 1 == 0) {return true;}
return false;
Expected: Green squares generate in random positions on the x- axis every 3 seconds and move upwards
Actual: Green squares all generate from the same random position on the X-axis.
You should reset X every time you're adding a new projectile:
if (gameArea.frameNo == 1 || everyinterval(150)) {
randomX = Math.floor(Math.random() * 480) + 15;
x = randomX;
y = gameArea.canvas.height;
projectiles.push(new component(40, 40, "green", x, y));
}
Otherwise, the randomX value stays constant as the value originally evaluated on line 1 when the interpreter reached it.
Here's your problem:
var randomX = Math.floor(Math.random() * 480) + 15;
// Generates a random number and stores it to randomX
// Called using 'randomX'
You need to turn it into a function if you want it to run each time:
var randomX = function() { Math.floor(Math.random() * 480) + 15 };
// Returns a new number each time
// Called using 'randomX()'
Both shivashriganesh mahato and natelouisdev have, essentially responded to how to fix the issue but since you are learning coding here is a tip. When you code, the code will run in a particular order. If you want something to be reassigned repeatedly, in this case a randomized number being used, and you want it to occur only after an event, you need to make sure that it gets trigger within each event.
natelouisdev has a good approach because, by using it as a function, you can call your randomizer more cleanly in your code and make it reassign the value of x each time.
Since you are building a game, it is also a good idea to compartmentalize your code. It'll make it easier to keep your ideas in order for each event trigger.
Example:
function gameLoss(){} - Define event return upon game loss. You can
then create editable rules to reason for loss without having to edit
the loss
function gameActive(){} - Defines what is normal gameplay. everything that occurs during normal gameplay should be managed here.
function gameArea(){} - Defines game canvas that function more for UI than for gameplay (scores, lifes, size of screen, etc)
Had you created individual functions you'd realize you only need a randomized 'x' value upon regular play thus you'd assign it within the gameActive() function and not as a global variable. Then you'd call the gameActive() function as many times as needed within a time interval to ensure a unique value is created each time.
-Side note: Don't litter unnecessary global variables. It'll make a mess off of your code when debugging. -

Canvas pixel manipulation optimization

I'm trying to do some real-time pixel manipulation (mode7-like transformations if you are interested) with canvas.
It works so: it takes a source image data that works like a frame buffer, it does some transformations and writes the result into a another 320x240 destination image data.
function transform() {
var src_pointer, dest_pointer;
var destData = dest_context.getImageData(0,0, 320, 240);
var imgdata = destData.data;
var sourceData= source_context.getImageData(0,0,320,240).data;
for (var y = 0; y< 240; y++) for (var x = 0; x< 320; x++) {
//*** DOING SOME TRANSFORMATIONS HERE, AND WRITE THE RESULT AT SX AND SY
//... Doesn't have impact in perfomance. Suspicious, huh?
dest_pointer = getPointer(x,y);
src_pointer = getPointer(sx, sy);
imgdata[dest_pointer] = sourceData[src_pointer];
imgdata[dest_pointer +1] = sourceData[src_pointer +1];
imgdata[dest_pointer +2] = sourceData[src_pointer +2];
// Alpha? Sad thing that canvas just handle 32bit image data. I don't really need it.
imgdata[dest_pointer +3] = sourceData[src_pointer +3];
}
dest_context.putImageData(destData,0,0);
}
//Function to help map a coordinate into image data array
function getPointer(x,y) {
return ( ( y * 320 ) + x ) * 4;
}
That have a poor perfomance if you execute it continuously (about 12 frames per second). Doing some profiling I discard an specific method bottleneck (getImageData and putImageData have a really little load time).
I used to think that issue was in the transformation section but the profiling throws me that the bottle neck was specifically in the pixel assignment. Maybe optimizing math operations can be possible (think that's hard, because that's in the border line between pure javascript and browser engine), but in array assignment is possible to optimize?

real-time processing web audio api

I am working with web audio api and requestAnimationFrame to visualize the audio input from microphone. I can successfully visualize the time-domain frequency data, but the problem is that since web audio api calculates the time in seconds, every second my interface changes depending on what the input is.
So my question is, how can I visualize the sound and make the graph stay on screen, thus I can see all my frequency data for a certain limit of time (let's say I speak and meanwhile visualize on canvas for only 5 seconds).
I am using the following code (took from examples here):
MicrophoneSample.prototype.visualize = function() {
this.canvas.width = this.WIDTH;
this.canvas.height = this.HEIGHT;
var drawContext = this.canvas.getContext('2d');
var times = new Uint8Array(this.analyser.frequencyBinCount);
this.analyser.getByteTimeDomainData(times);
for (var i = 0; i < times.length; i++) {
var value = times[i];
var percent = value / 256;
var height = this.HEIGHT * percent;
var offset = this.HEIGHT - height - 1;
var barWidth = this.WIDTH/times.length;
drawContext.fillStyle = 'purple';
drawContext.fillRect(i * barWidth, offset, 1, 1);
}
requestAnimFrame(this.visualize.bind(this));
}
getByteTimeDomainData does not give you frequency information. These are time domain waveform values in real time also known as amplitude values. If you want to visualize them over time append the values it into an array and draw that. If you want real frequency values use getByteFrequencyData.
OP, here's some pseudo code. FYI, this really isn't a web audio question, more of an animation question.
Store a variable / field in your visualizer prototype function that keeps track of how many seconds you want to delay the redrawing of your canvas, keep a separate counter that will increment everytime requestAnimFrame(...) gets drawn. Once your counter reaches your delay amount, then redraw the canvas.
Edit
Now that I think of it...the solution should be very simple. Correct me if I'm wrong, but this rough solution is assuming that you are calling MicrophoneSample.visualize() from within your animation loop...and therefore, the code therein executes every second. I could be of more help if you post your MicrophoneSample object code as well, or at least your animation loop.
/* NOTE!
*
*/
// Find a way to put these into your PARENT MicrophoneSample object
var delay = 5;
// Note that I am setting delayCount initially to zero - during the loop
// the delayCount will actually get reset to 1 from thereafter (not 0)...
// this gives us a way to initially draw your visualization on the first frame.
var delayCount = 0;
// Pull var times out so it doesn't get calculated each time.
var times = new Uint8Array(MicrophoneSample.analyser.frequencyBinCount);
// Same goes for the canvas...
// I would set these values inside of the PARENT MicrophoneSample object
MicrophoneSample.canvas.width = this.WIDTH;
MicrophoneSample.canvas.height = this.HEIGHT;
// you only need to establish the drawing context once. Do it in the PARENT
// MicrophoneSample object
var drawContext = this.canvas.getContext('2d');
MicrophoneSample.prototype.visualize = function() {
/*
* NOTE!
*/
// Here's the juicy meat & potatoes:
// only if the delayCount reaches the delay amount, should you UPDATE THE
// TIME DOMAIN DATA ARRAY (times)
// if your loop runs every second, then delayCount increments each second
// and after 5 seconds will reach your designated delay amount and update your
// times array.
if(delayCount == 0 || delayCount == delay) {
this.analyser.getByteTimeDomainData(times);
// Now, it would be redundant (and totally noob-programmer of you) to
// redraw the same visualization onto the canvas 5 times in a row, so
// only draw the visualization after the first pass through the loop and then
// every 5th pass after that :]
for (var i = 0; i < times.length; i++) {
var value = times[i];
var percent = value / 256;
var height = this.HEIGHT * percent;
var offset = this.HEIGHT - height - 1;
var barWidth = this.WIDTH/times.length;
drawContext.fillStyle = 'purple';
drawContext.fillRect(i * barWidth, offset, 1, 1);
}
// Note: 1, not 0!
delayCount = 1;
}
else {
delayCount++;
}
requestAnimFrame(this.visualize.bind(this));
}
And just keep in mind that I haven't actually tested any of this. But it should at least point you in the right direction.

canvas class javascript

I'm having trouble with my javascript code. I'm trying to create a moving set of circles where each circle has their own attributes. So far I've managed to input all the needed values into an array, but I can't figure out how to use them properly for drawing on canvas.
Here's the javascript:
var radius = 10;
var step = x = y = 0;
var r = g = b = 255;
var circleHolder = [];
var loop = setInterval(function(){update();}, 30);
function Circle(x, y, radius, r, g, b)
{
this.x = x;
this.y = y;
this.radius = radius;
this.r = r;
this.g = g;
this.b = b;
circleHolder.push(this);
}
Circle.prototype.draw = function()
{
Circle.prototype.ctx = document.getElementById("MyCanvas").getContext("2d");
Circle.prototype.ctx.clearRect(0,0,720,720); // clear canvas
Circle.prototype.ctx.beginPath();
Circle.prototype.ctx.strokeStyle = "rgb("+ this.r +", "+ this.g +", "+ this.b +")";
Circle.prototype.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
Circle.prototype.ctx.stroke();
}
Circle.prototype.update = function ()
{
step += .02;
step %= 2 * Math.PI;
this.x = parseInt((Math.sin(step)) * 150) + 360;
this.y = parseInt((Math.cos(step)) * 150) + 360;
this.radius += 16;
if (this.radius > 200)
{
for (i in circleHolder)
{
if (circleHolder[i]==this)
{
circleHolder.splice(i, 1);
}
}
}
}
function update()
{
var ci = new Circle(x, y, radius, r, g, b);
for (i in circleHolder)
{
ci = circleHolder[i];
ci.update();
ci.draw();
}
}
I'm pretty sure my problem lies within update() {} but I can't figure out how to do it properly.
EDIT: Okay, I've got it working with some changes! Check this Fiddle! I'm getting "ci not defined" error in the console though, and it has a strange bug: Changing the "if (this.radius > 128)" to higher integer it will make the circles spin faster, I don't know why. If you want you can try to change it to 256 and see what happens.
for (var i=0; i < allCircles; i++)
{
ci = circleHolder[i]; <----- This is causing the error
ci.update();
ci.draw();
}
it's not 100% clear to me what you're trying to do, but I tried to fix the main problem
One problem is your for loop.. you shouldn't use for in for arrays, do this instead:
for (var i=0 ; i<circleHolder.length ; i++)
{
ci = circleHolder[i];
ci.update();
ci.draw();
}
see this fiddle
Also I moved your get context and other things that should happen only once into the constructor, instead of having it in the update function.
You're also clearing the canvas before each draw, so the it will only show the last drawn circle per frame. (if you remove the clearRect it looks like one of those old spirographs).
You were also drawing the circles with (255,255,255)(white) so it wasn't showing until the color was changed.
Edit:
Really there are a few problems with this code:
The context shouldn't be inside a circle class if you plan on having many of them.
You should have some object which contains the canvas/context and an array of all circles.
Then have that object manage the updating/drawing.
For starters, unless there's something else going on, outside of this code:
You are using for ... in ... on an array, for-in is for objects, when used on arrays, most browsers will include methods like .splice and .forEach, and not just the numeric 0...n index.
function splice () {}.draw(); doesn't end well.
Also, what is the colour of your page's background? You're setting the rgb colour of each circle to 100% white. You're also clearing the canvas... ...which might well mean that the whole thing is transparent. So if you've got a transparent canvas, white circles and a white background, chances are great you're not going to be seeing anything at all, if this is even working without spitting out an error.
It might make a lot more sense to move your logic around in a way that lets you follow what's going on.
If you make a circle constructor, don't have it do anything but make a new circle.
Inside of your update, create a circle.
THEN put it inside of your circle collection (not in the circle constructor).
In a large application, you will typically call update on ALL objects, and then call draw on ALL objects, rather than updating and drawing one at a time.
Imagine a game that didn't bother to check if you had been hit by a bullet before drawing you and letting you move, for instance.
So inside of your loop, you should have an update and a draw.
Inside of the update, create your circles add them to the list and update the positions of them.
Inside of the draw, draw the circles.
In the future, this will give you the benefit of having things like collision-detection, without having to redraw everything, multiple times per frame.
Also, don't do DOM-access inside of a function that's going to be called many, many times (Circle.draw).
That will demolish your framerate in the future.
Instead, pass the function a dependency (the canvas).
// inside of the main game's scope
var screen = document.getElementById(...).getContext("2d");
// inside of your new draw function, in the animation/game loop
var i = 0, allCircles = circleHolder.length, currentCircle;
for (; i < allCircles; i += 1) {
currentCircle = circleHolder[i];
currentCircle.draw(screen);
}

Bilateral filter algorithm

I'm trying to implement a simple bilateral filter in javascript. This is what I've come up with so far:
// For each pixel
for (var y = kernelSize; y < height-kernelSize; y++) {
for (var x = kernelSize; x < width-kernelSize; x++) {
var pixel = (y*width + x)*4;
var sumWeight = 0;
outputData[pixel] = 0;
outputData[pixel+1] = 0;
outputData[pixel+2] = 0;
outputData[pixel+3] = inputData[pixel+3];
// For each neighbouring pixel
for(var i=-kernelSize; i<=kernelSize; i++) {
for(var j=-kernelSize; j<=kernelSize; j++) {
var kernel = ((y+i)*width+x+j)*4;
var dist = Math.sqrt(i*i+j*j);
var colourDist = Math.sqrt((inputData[kernel]-inputData[pixel])*(inputData[kernel]-inputData[pixel])+
(inputData[kernel+1]-inputData[pixel+1])*(inputData[kernel+1]-inputData[pixel+1])+
(inputData[kernel+2]-inputData[pixel+2])*(inputData[kernel+2]-inputData[pixel+2]));
var curWeight = 1/(Math.exp(dist*dist/72)*Math.exp(colourDist*colourDist*8));
sumWeight += curWeight;
outputData[pixel] += curWeight*inputData[pixel];
outputData[pixel+1] += curWeight*inputData[pixel+1];
outputData[pixel+2] += curWeight*inputData[pixel+2];
}
}
outputData[pixel] /= sumWeight;
outputData[pixel+1] /= sumWeight;
outputData[pixel+2] /= sumWeight;
}
}
inputData is from a html5 canvas object and is in the form of rgba.
My images are either coming up with no changes or with patches of black around edges depending on how i change this formula:
var curWeight = 1/(Math.exp(dist*dist/72)*Math.exp(colourDist*colourDist*8));
Unfortunately I'm still new to html/javascript and image vision algorithms and my search have come up with no answers. My guess is there is something wrong with the way curWeight is calculated. What am I doing wrong here? Should I have converted the input image to CIElab/hsv first?
I'm no Javasript expert: Are the RGB values 0..255? If so, Math.exp(colourDist*colourDist*8) will yield extremely large values - you'll probably want to scale colourDist to the range [0..1].
BTW: Why do you calculate the sqrt of dist and colourDist if you only need the squared distance afterwards?
First of all, your images turn out black/weird in the edges because you don't filter the edges. A short look at your code would show that you begin at (kernelSize,kernelSize) and finish at (width-kernelSize,height-kernelSize) - this means that you only filter a smaller rectangle inside the image where your have a margin of kernelSize on each side which is unfilterred. Without knowing your javscript/html5, I would assume that your outputData array is initialized with zero's (which means black) and then not touching them would leave them black. See my link the comment to your post for code that does handle the edges.
Other than that, follow #nikie's answer - your probably want to make sure the color distance is clamped to the range of [0,1] - youo can do this by adding the line colourDist = colourDist / (MAX_COMP * Math,sqrt(3)) (directly after the first line to calculate it). where MAX_COMP is the maximal value a color component in the image can have (usually 255)
I've found the error in the code. The problem was I was adding each pixel to itself instead of its surrounding neighbours. I'll leave the corrected code here in case anyone needs a bilateral filter algorithm.
outputData[pixel] += curWeight*inputData[kernel];
outputData[pixel+1] += curWeight*inputData[kernel+1];
outputData[pixel+2] += curWeight*inputData[kernel+2];

Categories

Resources