0

How I can make it work accurately so that each picture is exactly shown 50ms after the last one? (And so that the calculated time between seeing the picture and clicking is accurate?)

Background

I want to have a slideshow of images. The images are stored in the images directory and their names are sequential. The slideshow should start after the user clicks on the play button. And the user will click on the stop button a little after the 100th picture. And the code will show the user how many milliseconds after seeing the 100th picture they have clicked on the stop button. The problem is that when I run the code it doesn't work so accurate. It has some lag on some pictures. I was wondering

Here is my code:

<!DOCTYPE html>
<html>
    <head>
        <script>
var player;
var timestamp;
function preloadImage()
{
    var i = 1
    while(true){
        var img = new Image();
        img.src="images/" + i;
        i = i + 1;
        if (img.height == 0) { break; }
    }
}

function next(){
    var fullPath=document.getElementById("image").src;
    var filename = fullPath.split("/").pop();
    var n=parseInt(filename, 10) + 1;
    document.getElementById("image").src = "images/" + n;
    if (document.getElementById("image").height == 0) { clearInterval(player); }
    if (n == 100) { timestamp = new Date().getTime(); }
}

function start(){
    clearInterval(player);
    document.getElementById("image").src = "images/1";
    player = setInterval(function(){ next(); }, 50)
}

function stop(){
    clearInterval(player);
    alert("You clicked after " + (new Date().getTime() - timestamp) + "ms.")
}

preloadImage()
        </script>
    </head>
    <body>
            <img src="images/0" id="image"><br/>
            <button type="button" onclick='start()'>start</button>
            <button type="button" onclick='stop()'>stop</button>
    </body>
</html>
jpaugh
  • 6,634
  • 4
  • 38
  • 90
tgwtdt
  • 362
  • 2
  • 15
  • 1
    Note that, due to [Mozilla's recent response](https://blog.mozilla.org/security/2018/01/03/mitigations-landing-new-class-timing-attack/) to Spectre and Meltdown, getting precise timings is (at least temporarily) less possible than before. (Although, I'm not sure [performance.now](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now) would help you overcome the precision limitations of `setInterval`; you could use it to ensure events don't fire too early, but you could not guarantee a minimum response time.) – jpaugh Feb 05 '18 at 16:29
  • You cant do accurate timing in js. Thats how it is. – Jonas Wilms Feb 05 '18 at 16:33
  • @jpaugh while that is interesting and (possibly) relevant information, I believe the main issue sounds like, at least based on the description of the problem, that the images need to be prebuffered so that they don't lag the DOM thread as they're streaming via HTTP request while displaying. According to your link, getting millisecond resolution with `performance.now()` is still trivial, as 1ms >> 20us – Patrick Roberts Feb 05 '18 at 16:35
  • 1
    @tgwtdt your `preloadImage()` function may not work correctly. You can't synchronously load images, so checking `img.height` before `img.onload` is invoked might return `0` even if the image exists. It's possible your loop is breaking on the very first image and so none of the images are actually being preloaded. – Patrick Roberts Feb 05 '18 at 16:40
  • 1
    Why *exactly* 50ms? Would an alternate implementation with a `` and `requestAnimationFrame()` be a good alternative? – zero298 Feb 05 '18 at 16:48
  • @zero298 most browser implementations try to maintain a frame-rate of about 60fps with `rAF()`, which would put the amount of milliseconds between each frame at about 16-17. – Patrick Roberts Feb 05 '18 at 16:54
  • @PatrickRoberts Right, but it seems like the OP may be trying to do some kind of animation already considering they are trying to show ~20 images a second. This just sounds like an AB question. – zero298 Feb 05 '18 at 16:58
  • @JonasW. What is the timing accuracy I can get with js? – tgwtdt Feb 05 '18 at 18:56
  • `performance.now` is quite accurate, everything else `~ 2ms` maybe worse in some cases – Jonas Wilms Feb 05 '18 at 20:33

1 Answers1

1

As I stated in my comment, I believe your preloadImage() is not working as you expect. Try running the stack snippet below as a demonstration, and possibly make sure your cache is cleared:

function badPreloadImage () {
  var img = new Image();
  
  img.onload = function () {
    // check it asynchronously
    console.log('good', this.height);
  };
  img.src = 'https://www.w3schools.com/w3css/img_lights.jpg';
  // don't check height synchronously
  console.log('bad', img.height);
}

badPreloadImage();

To preload all your images properly, you must do so asynchronously:

function preloadImage (done, i) {
    if (!i) { i = 1; }

    var img = new Image();
    img.onloadend = function () {
        if (this.height == 0) { return done(); }
        preloadImage(done, i + 1);
    };
    img.src = "images/" + i;
}

// usage
preloadImage(function () { console.log('images loaded'); });

Other concerns

You should consider using performance.now() instead of new Date().getTime(), as it uses a more precise time resolution (currently 20us, as pointed out in this comment).

You might also consider storing references to each image as an array of Image objects, rather than loading each frame via specifying the src property of an HTMLImageElement, so that the browser can just load the data from memory, rather than loading from cache on the hard drive, or even making a new HTTP request when caching is not enabled.

Addressing each of these issues will allow you to measure timing more precisely by using the proper API and eliminating lag spikes on the DOM thread due to inefficient animation.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • I can't find out how to show images from an array. whatever I search gets me to using the src. Would you be so kind to teach me that? Actually I think I should ask a new question for that. I will now. Here is the link to my new [question](https://stackoverflow.com/q/48684703/9021273): – tgwtdt Feb 08 '18 at 11:42