Continuing this question: Task manager shows memory leak, but Heap snapshot doesn't
I managed to create a very simple example, which illustrates this leak, here is full source code:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>svg test</title>
<script type="text/javascript">
var svg;
var interval;
var svg;
window.onload = function(){
createSVG();
start();
}
function start(){
interval = setInterval(createElements, 100);
}
function createSVG(){
var div = document.getElementById("svgdiv");
div.innerHTML = "";
svg = createSvgElement("svg");
svg.style.position = "absolute";
svg.style.width = "600px";
svg.style.height = "500px";
svg.setAttribute("version", "1.1");
div.appendChild(svg);
createElements();
}
function createElements(){
removeElements();
for(var i = 0; i < 500; i++){
var element = createSvgElement("circle");
element.setAttribute("r", Math.random() * 10);
var transform = "translate(" + Math.round(Math.random() * 600) + "," + Math.round(Math.random() * 500) + ")";
element.setAttribute("transform", transform);
element.setAttribute("fill", "#CC0000");
svg.appendChild(element);
}
}
function removeElements(){
while(svg.hasChildNodes() ){
svg.removeChild(svg.lastChild);
}
}
function createSvgElement (name) {
return document.createElementNS("http://www.w3.org/2000/svg", name);
}
function stop(){
clearInterval(interval)
}
</script>
</head>
<body style="background-color:#FFFFFF">
<div id="svgdiv" style="width:600px; height:500px;"></div>
<input type="button" value="start" onclick="start()">
<input type="button" value="stop" onclick="stop()">
</body>
</html>
If I run this script, Chrome keeps eating the memory until it crashes. Other browsers don't. Instead of removing children one by one I also tried to clear it in a fast way by setting innerHTML="", but it's the same.
I enabled an experimental feature of Chrome which shows the kind of memory is used. The "Pages structures" memory is increasing a bit (however the HTML remains the same and there are no detached DOM objects), but the most memory goes to "Other" section.
If I stop the script and force GC to do it's job, the memory decreases by only few kilobytes. However if I wait for a minute or two, the memory is cleaned almost to a initial level. I know that my script is quite intense, but this happens also if I run it every 1 or two seconds only - I think that's quite enough for GC to do it's job. And I know GC is working, as other kind of memory is released. Could this be a Chrome bug? Maybe I should do something before removing the elements?
I managed to slow it down but it is still leaking. I also recreated the test using Raphael.js which did not leak. But when I was testing I found that it was when it was appending the circles. So Raphael must be doing something to stop it from leaking at that point.
Not sure, if it would be of much help, but if you define "var svg" only once,
then memory-consumption ( in Task manager) is not rising that fast
and also stop() function does work after that
This was indeed bug in Chrome, which was fixed some time later:
https://bugs.chromium.org/p/chromium/issues/detail?id=172221&can=4&q=svg&colspec=ID%20Pri%20Mstone%20ReleaseBlock%20OS%20Area%20Feature%20Status%20Owner%20Summary
Related
This is like the question at:
Sound effects in JavaScript / HTML5
but I'm just seeking a specific answer to the issue of repeatability.
The above question and other similar ones have helpful answers to use the following javascript:
function beep1()
{ var snd = new Audio("file.wav"); // buffers automatically when created
snd.play();
}
or even more self-contained, you can now include a wav in-line, such as:
function beep2()
{ var snd = new Audio("data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=");
snd.play();
}
When I tried these examples, I could only get the sound to play once on my computer. I noticed this was a common complaint in multiple questions, but never saw an answer to it.
The closest thing to an answer was in the referenced question in which #Kornel stated:
To play same sound multiple times, create multiple instances of the Audio object.
You could also set snd.currentTime=0 on the object after it finishes playing.
If this is the key to my puzzle, I don't quite understand it. (I don't know how to destroy / release an audio object.) Can someone show me exactly how to get a button to keep replaying my sound every time it is clicked, either using one of the suggestions of #Kornel or some other way?
Put the sound references outside of the function. Otherwise, each time you call the function, a new sound object is created.
var snd1 = new Audio("file.mp3");
var snd2 = new Audio("data:audio/mpeg;base64,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wZ0G7jqlt/OcDIKX+/1NblSX+VKfQfP8xuJJGk7////rf////+PgXTv///1JThJJQainmySAB6imUyuVbVttUo7T4Csa821OuF88f62+CZHFnGf///mQgYIEO0SMF2NVy9NxYTdlqJ8AuS4zr//SJoTUJ+CaKKTcZvosrUPo8W/MUv0f033E9E/QpN6P///v/////WRR2mwUAYUABjabRu1vrOLKAF0kIdHjnEx/iNWo7jGn1////mApxNTJQQOU1Het/NoUFTMQs6Vja///THaGIl/0fojl8mjd/Jo8W+ZfpNpCajsz7////6kn/////WRRgDz//LD1KSTDjKOciSAKxdLx5S31uYqKIWj/+5JECgAC8V5M6g9rdFyr6Vo9rW6KtHcr5DEJQRkSpLRklSigvVc4QpmyPe9H3zHR1/in9P/8VNCMJOzYUDyVjfwHP0ZgiZt/3/+9EBnDKbegdUrckhgntHaQ9vX/X/9A/////+r/////mJ3/9ItRcoVRogAcmV9N8z0pvES8QQsKoMGXEymPQyWm6E4HQLqgpv/CZJAtYXQSwoF8e6SB56zABEoW+qgZjJAZovGr0Gl5/OjFKL3JwnaX9v7/X8y1f/////////49WAzMzEYYMZLq6CUANIqbDX7lisBIdraAEPwShTRc9WZ2vAqBc4NQ9GrUNaw0Czcrte0g1NEoiU8NFjx4NFh54FSwlOlgaCp0S3hqo8SLOh3/63f7P/KgKJxxhgGSnAFMCnIogwU5JoqBIDAuBIiNLETyFmiImtYiDTSlb8ziIFYSFv/QPC38zyxEOuPeVGHQ77r/1u/+kq49//6g4gjoVQSUMYQUSAP8PwRcZIyh2kCI2OwkZICZmaZxgnsNY8DmSCWX0idhtz3VTJSqErTSB//1X7TTTVVV//uSZB2P8xwRJ4HvYcItQlWBACM4AAABpAAAACAAADSAAAAEVf/+qCE000VVVVU0002//+qqqqummmmr///qqqppppoqqqqppppoqqATkEjIyIxBlBA5KwUEDBBwkFhYWFhUVFfiqhYWFhcVFRUVFv/Ff/xUVFRYWFpMQU1FMy45OS41qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg==");
function beep1() {
snd1.play();
}
function beep2() {
snd2.play();
}
beep1();
setInterval(beep2, 300);
We can create audio waveforms with the web audio api.
The following example shows a bip, made of two tones, one is at 233hz for 100ms with a gain of 10DB, the second is at 603Hz with a duration of 200ms, with 3DB of gain.
Both sounds are in a loop of 1500ms, with the help of setInterval.
This is a minimal example, it can't be stopped! (As asked!)
// gain, frequency, duration
let a = new AudioContext()
function k(w,x,y){
console.log("Gain:"+w, "Hz:"+x, "ms:"+y)
v = a.createOscillator()
u = a.createGain()
v.connect(u)
v.frequency.value = x
v.type = "square"
u.connect(a.destination)
u.gain.value = w * 0.01
v.start(a.currentTime)
v.stop(a.currentTime + y *0.001)
}
setInterval(function(){ k(10,233,100); k(3,603,200)}, 1500)
There is ways to create much more complex 8bits songs in few lines, with differents loops of many duration and tones:
let a = new AudioContext()
function k(w,x,y){
console.log("Gain:"+w, "Hz:"+x, "ms:"+y)
let v = a.createOscillator()
let u = a.createGain()
v.connect(u)
v.frequency.value = x
v.type = "square"
u.connect(a.destination)
u.gain.value = w * 0.01
v.start(a.currentTime)
v.stop(a.currentTime + y *0.001)
}
setInterval(function(){ k(10,233,100); k(3,603,200)}, 1000)
setInterval(function(){ k(8,1646,100); k(8,1444,100) }, 500)
setInterval(function(){ k(8,728,100); k(8,728,100) }, 3000)
setInterval(function(){ k(8,728,100); k(8,728,100) }, 3000)
setInterval(function(){ k(8,364,100); k(8,364,100) }, 6000)
setInterval(function(){ k(8,364,100); k(8,157,200) }, 12000)
We can as well set the values in an array, and loop trough it.
With for, for..in, while, do..while, much more complex, but to create patterns, arpeggios, etc.
Someone, one day will enjoy this ;)
What about using the new Audio API. (No Microsoft support!)
var audioContext = AudioContext && new AudioContext();
function beep(amp, freq, ms){//amp:0..100, freq in Hz, ms
if (!audioContext) return;
var osc = audioContext.createOscillator();
var gain = audioContext.createGain();
osc.connect(gain);
osc.value = freq;
gain.connect(audioContext.destination);
gain.gain.value = amp/100;
osc.start(audioContext.currentTime);
osc.stop(audioContext.currentTime+ms/1000);
}
Didn't you try this
Just the HTML5 thing
<audio controls loop>
<source src="horse.ogg" type="audio/ogg">
<source src="horse.mp3" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
I needed an "alert" for a web app that will be used predominately on a mobile device. The new autoplay policy https://developers.google.com/web/updates/2017/09/autoplay-policy-changes, was giving me a problem so I did this...
Top of html code to play the "alert" you need a button so the user interacts with the page and authorizes future use of audio.
<p align="center" id="snd_btn"><button id="allow_alert" class="btn btn-danger" onclick="play_sound();"> Allow Alert </button></p>
Add'l html...................
then later in the script section I have ....
<script>
function play_sound(){
snd.play(); // plays the "1-second-of-silence.mp3"
$("#snd_btn").hide(); // this hides the prompt "Allow Alert"
}
snd = new Audio("sound/1-second-of-silence.mp3");
snd2 = new Audio("sound/PHONERNG.WAV");
the as part of an online event (map coordinates update) this is included in the ajax success function ...
success: function(data){
if(data['badge'] > 0){
$("#vt_badge").html(data['badge']);
snd2.play(); //Plays the "alert" sound
}
}
'''''
</script>
Th "allow alert" button is only there on page load so the user interacts with it, and it plays a sound, "1-second-of-silence.mp3". That satisfies the player's requirement for page interaction, now the actual alert, "PHONERNG.WAV" plays automatically as required.
MrFitz
This works better for me.
Just follow the snippet below
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>Press the Button</h1>
<button onclick="play()">Press Here!</button>
<script>
function play() {
var audio = new Audio(
'https://media.geeksforgeeks.org/wp-content/uploads/20190531135120/beep.mp3');
audio.play();
}
</script>
</body>
</html>
Hej,
i have this plunker: http://plnkr.co/edit/bKzi6rU3lIXT4Nz2wkR8?p=info
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="stilovi/main.css">
<title>Testing</title>
</head>
<body>
<div id="avengers"></div>
</body>
<script src="snap.svg-min.js"></script>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://rawgit.com/julianshapiro/velocity/master/velocity.min.js"></script>
<script>
function fetchXML(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function(evt) {
//Do not explicitly handle errors, those should be
//visible via console output in the browser.
if (xhr.readyState === 4) {
callback(xhr.responseXML);
}
};
xhr.send(null);
};
/*var list = ["test3.svg","test.2svg"];*/
//fetch the document
fetchXML("test3.svg", function(newSVGDoc) {
//import it into the current DOM
var n = document.importNode(newSVGDoc.documentElement, true);
document.getElementById("avengers").appendChild(n);
var ironman = document.getElementsByTagName("polygon");
var ironmanlist = Array.prototype.slice.call(ironman);
/*alert(ironmanlist.length);*/
ironmanlist.forEach(function(elem, i) {
/*for (var index = 0; index < elem.points.length; ++index){
//2.test case morphing each point (not working)//
console.log(elem.points[index]);
$.Velocity(elem.points[index],{x:100,y:100},{duration:Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000}, "ease-in-out");
//3.test case morphing each point in another way (not working)//
/*$(elem.points[index])
.velocity({x:100,y:100},{duration:Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000}, "ease-in-out");
console.log(elem.points[index]);
}*/
//1. working test case (translation)//
console.log(elem.points[0].x);
$.Velocity(elem, {
translateX: -300
}, {
duration: Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000
}, "ease-in-out");
//$.Velocity(elem,{rotateZ:"45deg"},{duration:Math.floor(Math.random() * (3000 - 1000 + 1)) + 1000}, "ease-in-out");
console.log(elem.points[0].x);
//End of 1. working test case//
});
console.log(ironmanlist);
});
</script>
</html>
With my code, and some examples. What I want to do is morph each polygon from one SVG image, into another polygon from another SVG image. The translation works, but I'm not sure how to do a morph.
Can anyone help, or check the code and tell me what I am doing wrong?
There are a lot of polygons, and I need it to be fast so i went with velocity.js for this.
I was also thinking of maybe moving it all to three.js, and maybe convert it to a format that would be best to use with three.js. But if it is a possibilty to use it as svg and keep a great performance i would do so.
As far as I'm aware, velocity.js doesn't support this as (taken from their website):
In general, Velocity can animate any property that takes a single numeric value.
As SVG paths usually (always?) consist of multiple values, morphing is way beyond their current scope.
However, I can highly recommend Snap for morphing SVG paths.
I have a loop which needs to be run 200 million times in a browser. It's a simulator which several people need to use regularly. It takes about 15 minutes to run, but during this time, the browsers will frequently pop up a warning with "this script is taking too long" etc., and it completely hangs Firefox during the function. This also means the page does not update my status indicator (which is just a number).
I have googled "javascript yield" and read the first 4 pages of hits. Some discuss a new "yield" keyword, but there is only one description and example, which I find incomprehensible, e.g. "The function containing the yield keyword is a generator. When you call it, it's formal parameters are bound to actual arguments, but it's body isn't actually evaluated". Does yield yield to the UI?
One of the few solutions I did find is this old post which uses the deprecated callee argument and a timer to call itself:
http://www.julienlecomte.net/blog/2007/10/28/
However, the above example doesn't contain any loop variables or state, and when I add these it falls apart, and my net result is always zero.
It also doesn't do chunking, but I have found some other examples which chunk using "index % 100 == 0" on every iteration. However, this seems to be a slow way of doing it. E.g. this:
How to stop intense Javascript loop from freezing the browser
But it doesn't have any way to update progress, and doesn't yield to the UI (so still hangs the browser). Here is a test version which hangs the browser during execution:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
var spins = 1000000
var chunkSize = 1000;
var chunk;
function Stats() {this.a=0};
var stats = new Stats();
var big;
var index = 0;
var process = function() {
for (; index < spins; index++) {
stats.a++;
big = (big/3.6)+ big * 1.3 * big / 2.1;
console.write(big);
// Perform xml processing
if (index + 1 < spins && index % 100 == 0) {
document.getElementById("result").innerHTML = stats.a;
setTimeout(process, 5);
}
}
document.getElementById("result").innerHTML = stats.a;
};
</script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body onload="process()">
<div id=result>result goes here.</div>
</body>
</html>
and here is another attempt which the stats.a is always zero (So I presume there is some scoping issue):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
var spins = 1000000
var chunkSize = 1000;
var chunk;
function Stats() {this.a=0};
var stats = new Stats();
function doIt() {
function spin() {
for (spinIx=0; (spinIx<chunkSize) && (spinIx+chunk < spins); spinIx++) {
stats.a++;
}
}
for (chunk =0; chunk < spins; chunk+=chunkSize){
setTimeout(spin, 5);
document.getElementById("result").innerHTML = stats.a;
}
document.getElementById("result").innerHTML = stats.a;
}
</script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body onload="doIt()">
<div id=result>result goes here.</div>
</body>
</html>
I've spent 48 hours trying to get this working - either I am very dumb or this is very hard. Any ideas?
Several people have suggested web workers. I tried several days to get his working, but I could not find a similar example which passes a number etc. The code below was my last attempt to get it working, but the result is always 0 when it should be 100000. I.e. it fails in the same way that my second example above fails.
spinMaster.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<script>
if(typeof(Worker)==="undefined") {
document.write("<h1>sorry, your browser doesnt support web workers, please use firefox, opera, chorme or safari</h1>");
}
var worker =new Worker("spinWorker.js");
worker.postMessage({times:1000000});
worker.onmessage=function(event){
document.getElementById("result").innerHTML=event.data;
};
</script>
<div id="result">result goes here</div>
</body>
</html>
spinWorker.js
function State() {
this.a=0;
}
var state = new State();
self.addEventListener('message', spin, false);
function spin(e) {
var times, i;
times = e.data.times;
//times = 1000000; // this doesnt work either.
for(i;i<times;i++) {
state.a++;
}
self.postMessage(state.a);
}
resultant output: 0
Web workers sound like the better solution.
I wrote this up quickly so i dunno if itll work. Performance will be very bad...
Edit: Changed to same format as poster. Tested
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
var spins = 1000000
var chunkSize = 1000;
var chunk;
function Stats() {this.a=0};
var stats = new Stats();
var big = 0.0;
var index = 0;
function workLoop() {
index += 1;
stats.a++;
big = (big/3.6)+ big * 1.3 * big / 2.1;
console.log(big);
// Perform xml processing
document.getElementById('result').innerHTML = stats.a;
if (index < spins) {
setTimeout(workLoop, 5);
}
}
</script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body onload="workLoop()">
<div id="result">result goes here.</div>
</body>
</html>
since JS is single threaded usually, I don't think there is any way around it. If you are supporting only newer browsers however, you might want to look into web workers, which can spawn a new thread to do work: http://developer.mozilla.org/en-US/docs/DOM/Using_web_workers
the only downside I can think of is that I've read it is hard to debug Web Workers because dev tools (Chrome Dev tools unless running dev channel, firebug), doesn't support profiling for it.
here's a nice tutorial to get you started: http://net.tutsplus.com/tutorials/javascript-ajax/getting-started-with-web-workers/
You have close to a working example in your 1st test, but I do see a logic error. In your if(), you need to return from the function otherwise it'll always be running multiple functions competing for that thread.
var process = function() {
for (; index < spins; index++) {
stats.a++;
big = (big/3.6)+ big * 1.3 * big / 2.1;
console.write(big);
// Perform xml processing
if (index + 1 < spins && index % 100 == 0) {
document.getElementById("result").innerHTML = stats.a;
setTimeout(process, 5);
//!!!!!
return;//without this it'll keep iterating through the for loop without waiting for the next 5ms
//!!!!!
}
}
document.getElementById("result").innerHTML = stats.a;
};
Neither of the samples as-is wait for the next setTimeout (in your 2nd Example you continue iterating through your for loop until the for loop is completed, but at each block size you set a timeout for a subsequent iteration. All this does is delay the entire sequence for 5 ms, they still all pileup and execute 5 ms from when your for loop begins iterating)
All in all you seem to be on the right track, there are just small logic errors in both examples.
There is a library on github available for this type of big looping without locking the browser/thread and without using web-workers.
Somewhat experimental, and supports big recursive loops, but it may work for you. Depends on q.js for resolving promises.
https://github.com/sterpe/stackless.js
This should do what you want:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
var spins = 1000000
var chunkSize = 1000;
var stats = {a:0,spins:0};
function doIt() {
for (var chunk = 0; chunk < chunkSize; chunk++) {
stats.a++;
}
document.getElementById("result").innerHTML = stats.a;
if(++stats.spins < spins) setTimeout(doIt,5);
}
</script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body onload="doIt()">
<div id=result>result goes here.</div>
</body>
</html>
I have been trying to set up a javascript game loop and I have two issues I am running into. I find that in chrome when I lose focus of the browser window and then click back the animation I have running does this weird "catch up" thing where it quickly runs through the frames it should of been rendering in the background. I also have noticed that the animation is blury when moving at the current speed I have it at yet other people have been able to get their canvas drawings to move quickly and still look crisp. I know their seems to be a lot out about this but I cant make sense of what my issue really is. I thought this was a recommended way to create a game loop.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Frame Test</title>
<link href="/css/bootstrap.css" media="all" rel="stylesheet" type="text/css" />
<script language="javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"
type="text/javascript">
</script>
<script language="javascript" src="js/jquery.hotkeys.js" type="text/javascript"></script>
<script language="javascript" src="js/key_status.js" type="text/javascript"></script>
<script language="javascript" src="js/util.js" type="text/javascript"></script>
<script language="javascript" src="js/sprite.js" type="text/javascript"></script>
</head>
<body>
<button id="button1">
Toggle Loop</button>
<h1 id="frameCount">
Game Loop Test</h1>
<canvas id="gameCanvas" width="800" height="500">
<p>Your browser doesn't support canvas.</p>
</canvas>
<script type='text/javascript'>
// demo code used for playing around with javascript-canvas animations
var frameCount = 0;
var drawingCanvas = document.getElementById('gameCanvas');
// Check the element is in the DOM and the browser supports canvas
if (drawingCanvas.getContext) {
var context = drawingCanvas.getContext('2d');
var x = 100;
var y = 100;
var right = true;
context.strokeStyle = "#000000";
context.fillStyle = "Green";
context.beginPath();
context.arc(x, y, 50, 0, Math.PI * 2, true);
context.closePath();
context.stroke();
context.fill();
}
function Timer(settings) {
this.settings = settings;
this.timer = null;
this.on = false; //Bool that represents if the timer is running or stoped
this.fps = settings.fps || 30; //Target frames per second value
this.interval = Math.floor(1000 / 30);
this.timeInit = null; //Initial time taken when start is called
return this;
}
Timer.prototype =
{
run: function () {
var $this = this;
this.settings.run();
this.timeInit += this.interval;
this.timer = setTimeout(
function () { $this.run() },
this.timeInit - (new Date).getTime()
);
},
start: function () {
if (this.timer == null) {
this.timeInit = (new Date).getTime();
this.run();
this.on = true;
}
},
stop: function () {
clearTimeout(this.timer);
this.timer = null;
this.on = false;
},
toggle: function () {
if (this.on) { this.stop(); }
else { this.start(); }
}
}
var timer = new Timer({
fps: 30,
run: function () {
//---------------------------------------------run game code here------------------------------------------------------
//Currently Chorme is playing a catch up game with the frames to be drawn when the user leaves the browser window and then returns
//A simple canvas animation is drawn here to try and figure out how to solve this issue. (Most likely related to the timer implimentation)
//Once figured out probably the only code in this loop should be something like
//updateGameLogic();
//updateGameCanvas();
frameCount++;
if (drawingCanvas.getContext) {
// Initaliase a 2-dimensional drawing context
//Canvas commands go here
context.clearRect((x - 52), 48, (x + 52), 104);
// Create the yellow face
context.strokeStyle = "#000000";
context.fillStyle = "Green";
context.beginPath();
if (right) {
x = x + 6;
if (x > 500)
right = false;
} else {
x = x - 6;
if (x < 100)
right = true;
}
context.arc(x, 100, 50, 0, Math.PI * 2, true);
context.closePath();
context.stroke();
context.fill();
}
document.getElementById("frameCount").innerHTML = frameCount;
//---------------------------------------------end of game loop--------------------------------------------------------
}
});
document.getElementById("button1").onclick = function () { timer.toggle(); };
frameCount++;
document.getElementById("frameCount").innerHTML = frameCount;
</script>
</body>
</html>
-------------Update ---------------------
I have used requestanimation frame and that has solved the frame rate problam but I still get weird ghosting/bluring when the animation is running. any idea how I should be drawing this thing?
Okay, so part of your problem is that when you switch tabs, Chrome throttles down its performance.
Basically, when you leave, Chrome slows all of the calculations on the page to 1 or 2 fps (battery-saver, and more performance for the current tab).
Using setTimeout in the way that you have is basically scheduling all of these calls, which sit and wait for the user to come back (or at most are only running at 1fps).
When the user comes back, you've got hundreds of these stacked calls, waiting to be handled, and because they've all been scheduled earlier, they've all passed their "wait" time, so they're all going to execute as fast as possible (fast-forward), until the stack is emptied to where you have to start waiting 32ms for the next call.
A solution to this is to stop the timer when someone leaves -- pause the game.
On some browsers which support canvas games in meaningful ways, there is also support for a PageVisibility API. You should look into it.
For other browsers, it'll be less simple, but you can tie to a blur event on the window for example.
Just be sure that when you restart, you also clear your interval for your updates.
Ultimately, I'd suggest moving over to `requestAnimationFrame, because it will intelligently handle frame rate, and also handle the throttling you see, due to the stacked calls, but your timer looks like a decent substitute for browsers which don't yet have it.
As for blurriness, that needs more insight.
Reasons off the top of my head, if you're talking about images, are either that your canvas' width/height are being set in CSS, somewhere, or your sprites aren't being used at a 1:1 scale from the image they're pulled from.
It can also come down to sub-pixel positioning of your images, or rotation.
Hope that helps a little.
...actually, after looking at your code again, try removing "width" and "height" from your canvas in HTML, and instead, change canvas.width = 800; canvas.height = 500; in JS, and see if that helps any.
I'm starting to program a javascript tower defense; so far i have the movement of the minions over a trajectory. But I have a very big trouble, the game suddenly freezes for a couple of seconds. I'm guessing that is the garbage collector doing its job, any ideas on how i can solve this will be very good since i plan on adding a lot more of elements to the game and i don't want to keep coding till i get this flowing perfectly!
The code so far is pretty simple; you can check it out here
Here's the code:
<html>
<head>
<style>
#game{
background:red;
width:500px;
height:500px;
position:relative;
}
.mostro {
background:black;
width:15px;
height:15px;
position:absolute;
}
</style>
</head>
<body>
<div id="game">
<script type="text/javascript">
waypoint_x = [40, 140, 140, 220, 220, 80, 80, 340, 340, 420, 420];
waypoint_y = [140, 140, 60, 60, 240, 240, 320, 320, 100, 100, -20];
delay = 25;
new_monster = 0;
monsters_placed = 0;
monsters = [];
var d = new Date();
dist_x = 0;
dist_y = 0;
angle = 0;
mostro="";
total_monsters = 5;
function runGame() {
if (monsters_placed<total_monsters) {
new_monster++;
}
if (new_monster == delay) {
new_monster = 0;
document.getElementById("game").innerHTML = document.getElementById("game").innerHTML + '<div class="mostro" id="mostro-'+monsters_placed+'"></div>';
monsters_placed++;
}
for (i=0;i<monsters_placed;i=i+1) {
mostro = monsters[i];
dist_x = waypoint_x[mostro.point_to_reach] - mostro._x;
dist_y = waypoint_y[mostro.point_to_reach] - mostro._y;
if ((Math.abs(dist_x) + Math.abs(dist_y)) < 1) {
monsters[i].point_to_reach++;
}
angle = Math.atan2(dist_y, dist_x);
mostro._x = mostro._x + mostro.speed * Math.cos(angle);
mostro._y = mostro._y + mostro.speed * Math.sin(angle);
monsters[i]._rotation = angle/Math.PI*180-90
document.getElementById("mostro-"+i).style.left = Math.ceil(mostro._x) + "px";
document.getElementById("mostro-"+i).style.top = Math.ceil(mostro._y) + "px";
}
}
function setUpGame(){
for(i=0;i<=total_monsters;i++){
monsters[i] = new Object();
monsters[i].point_to_reach = 0;
monsters[i].speed = 1;
monsters[i]._x = 0;
monsters[i]._y = 0;
}
}
setUpGame();
setInterval(runGame,10);
</script>
</body>
</html>
Its not the garbage collector doing the job but in your code when you try to set the top and left positions, at a particuar time the value that you try to set in not a number. So the code breaks....
I think this occurs when the moving div crosses the top of the container with red background.
Yes, thats right: the delay is because when there are too many monsters, there are too many position updates that need to be done. This causes the "redraw" delay..
I see that there is a DOM element for each monster(as should be the case). But, you are updating their positions one by one.
Tips to reduce this lag:
Firstly, it would be a better stategy to update their positions en masse:
<div id='monster-container'>
<div id='monstser-1'></div>
<div id='monstser-2'></div>
<div id='monstser-3'></div>
</div>
So update the position of '#monster-container' when the monsters move. This way redraw time will definitely be minimized. What I say is from a primitive understanding of your game. You may need to modify this approach depending upon the path of the monsters. My approach will work directly only if the monsters only move in a straight line.
Secondly, if you are using img's for the monsters, consider using div's, and set the images as backgrounds of the div. This has given faster redraw performance in many of my pet games.
Thirdly, if you are using individual images for the monsters, consider using a composite image and CSS spriting.
Wish you luck with your game! Cheers!!
jrh
Yes, that's definitely a garbage collector. I am developing a JavaScript game myself, and I spent last few days trying to get rid of this problem. So far I can say that it's impossible.
However, I would like to note that different browsers have different garbage collectors, and for example, in Safari 4, your example runs perfectly smooth.
And here is interesting link on this topic: Reducing freezing with Object Pooling
Honestly, I think, that technique described in that article is not very helpful, because even in your example, that doesn't have any variables needed to be cleared, freezing is really noticeable.
Also I've rewritten your example, to test if global variables ruined performance.
You can see the difference yourself