title: Load and draw tiled images to canvas in JavaScript
body: <p>It seems clear to me how to dynamically load and draw an image with JavaScript. I attach an <code>onload</code> function and set the image source:</p>
  
  <pre><code>var img = new Image();
  img.onload = function() {
      ctx.drawImage(img, 0, 0);
  }
  img.src = "your_img_path";
  </code></pre>
  
  <p>I want to do this for a 2D array of images (tiles) and update those tiles many times.  However, as described later, I run into issues with implementing this.</p>
  
  <p>The function that I've written, and which I show below, contains some computations that are not relevant to my current problem. Their purpose is to allow for the tiles to be panned/zoomed (simulating a camera). The tile chosen to be loaded depends on the location requested to render.</p>
  
  <pre><code>setInterval(redraw, 35);
  
  function redraw()
  {
      var canvas = document.getElementById("MyCanvas");
      var ctx = canvas.getContext("2d");
  
      //Make canvas full screen.
      canvas.width  = window.innerWidth;
      canvas.height = window.innerHeight;
  
      ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
  
      //Compute the range of in-game coordinates.
      var lowestX = currentX - (window.innerWidth / 2) / zoom;
      var highestX = currentX + (window.innerWidth / 2) / zoom;
      var lowestY = currentY - (window.innerHeight / 2) / zoom;
      var highestY = currentY + (window.innerHeight / 2) / zoom;
  
      //Compute the amount of tiles that can be displayed in each direction.
      var imagesX = Math.ceil((highestX - lowestX) / imageSize);
      var imagesY = Math.ceil((highestY - lowestY) / imageSize);
  
      //Compute viewport offsets for the grid of tiles.
      var offsetX = -(mod(currentX, imageSize) * mapZoom);
      var offsetY = -(mod(currentY, imageSize) * mapZoom);
  
      //Create array to store 2D grid of tiles and iterate through.
      var imgArray = new Array(imagesY * imagesX);
      for (var yIndex = 0; yIndex &lt; imagesY; yIndex++) {
          for (var xIndex = 0; xIndex &lt; imagesX; xIndex++) {
              //Determine name of image to load.
              var iLocX = (lowestX + (offsetX / mapZoom) + (xIndex * imageSize));
              var iLocY = (lowestY + (offsetY / mapZoom) + (yIndex * imageSize));
              var iNameX = Math.floor(iLocX / imageSize);
              var iNameY = Math.floor(iLocY / imageSize);
  
              //Construct image name.
              var imageName = "Game/Tiles_" + iNameX + "x" + iNameY + ".png";
  
              //Determine the viewport coordinates of the images.
              var viewX = offsetX + (xIndex * imageSize * zoom);
              var viewY = offsetY + (yIndex * imageSize * zoom);
  
              //Create Image
              imgArray[yIndex * imagesX + xIndex] = new Image();
              imgArray[yIndex * imagesX + xIndex].onload = function() {
                  ctx.drawImage(imgArray[yIndex * imagesX + xIndex], viewX, viewY, imageSize * zoom, imageSize * zoom);
              };
              imgArray[yIndex * imagesX + xIndex].src = imageName;
          }   
      }    
  }
  </code></pre>
  
  <p>As you can see, there is an array declared in the function to equal the size of the displayed grid of tiles.  The elements of the array are ostensibly filled with images that are drawn once dynamically loaded.  Unfortunately, <code>imgArray[yIndex * imagesX + xIndex]</code> is not considered an image by my browser, and I receive the error:</p>
  
  <blockquote>
    <p>Uncaught TypeError: Failed to execute 'drawImage' on 'CanvasRenderingContext2D': The provided value is not of type '(HTMLImageElement or HTMLVideoElement or HTMLCanvasElement or ImageBitmap)'</p>
  </blockquote>
  
  <p>Also, I had originally thought the array wasn't necessary.  After all, why should I need to keep a handle to the image after I've finished drawing it if I'm just going to load a new one next time <code>redraw</code> is called?  In this scenario, the loop looked like this:</p>
  
  <pre><code>for (var yIndex = 0; yIndex &lt; imagesY; yIndex++) {
      for (var xIndex = 0; xIndex &lt; imagesX; xIndex++) {
          //Determine name of image to load.
          var iLocX = (lowestX + (offsetX / mapZoom) + (xIndex * imageSize));
          var iLocY = (lowestY + (offsetY / mapZoom) + (yIndex * imageSize));
          var iNameX = Math.floor(iLocX / imageSize);
          var iNameY = Math.floor(iLocY / imageSize);
  
          //Construct image name.
          var imageName = "Game/Tiles_" + iNameX + "x" + iNameY + ".png";
  
          //Determine the viewport coordinates of the images.
          var viewX = offsetX + (xIndex * imageSize * zoom);
          var viewY = offsetY + (yIndex * imageSize * zoom);
  
          //Create Image
          var img = new Image();
          img.onload = function() {
              ctx.drawImage(img, viewX, viewY, imageSize * zoom, imageSize * zoom);
          };
          img.src = imageName;
      }   
  }
  </code></pre>
  
  <p>I had better luck with this method since the last processed tile was rendered.  Despite this, all other tiles were not drawn.  I guess I might have been overwriting things so that only the last kept its values.</p>
  
  <ol>
  <li><p>So, what can I do so that I can render a 2D grid of tiles repeatedly such that the tiles are loaded all the time?</p></li>
  <li><p>And why is the array element not considered an image?</p></li>
  </ol>