2
\$\begingroup\$

Kindly help me check this code, I want to be able to make it load thousands/millions of different pictures as HTML background without crashing or slowing down the browser.

The images can be created dynamically, or linked to its folder, I was able to do that for up to 1000 images/colors per canvas, and add up many more canvas, but at 30 canvas, which is 30,000 images/colors the browser stops to respond(slowish). Tested only on google chrome, so help me achieve tens of millions random colors/pictures.

FYI: The images must be displayed all at once.

Rihana Photo Mosaic or picturemosaics this is similar to what i want to achieve, as you can see about 36,000 images without any performance issue, they even boast of more than 5 million pictures. Mine should be easier because my pictures are to be random or in a pattern not necessarily being in a mosaic form. If you have another method or way to achieve it, that would be helpful too.

<html>
<body>
  <script>
  var cInt=0;
  var loopCInt=0;
  while (loopCInt<3){
   var canvasName = document.createElement("canvas");
   canvasName.setAttribute('width', "1000");
   canvasName.setAttribute('height', "1000");
   var canvasIdName="myCanvas"+cInt;
   canvasName.setAttribute('id', canvasIdName);
   document.body.appendChild(canvasName);
    var c = document.getElementById(canvasIdName);
    var ctx = c.getContext("2d");
    var i = 0;
    var right=0;
    var tops=0;
    var goDown=0;
    while (i <= 1000) {
      ctx.beginPath();
      ctx.rect(right, goDown, 30, 30);
      ctx.fillStyle = '#'+Math.random().toString(16).slice(-6);
      ctx.fill();
      i++;
      right=right+30;
      tops++;
      if (tops==30){
        goDown=goDown+30;
        right=0;
        tops=0;
      }
    }

  loopCInt++;
  cInt++;
}
</script>
</body>
</html>
\$\endgroup\$

3 Answers 3

10
\$\begingroup\$

Sluggishness

Memory limits

Take a look at a random folder of images on your computer, and observe it's size. Thousands of images will amount to gigabytes of space, which means all that needs to be loaded into memory. You can simply not do that reliably.

There is also not really a point in rendering things that the user cannot see.

Thousands or millions of images?

Consider why you think you need to display thousands or millions of images. Consider a pretty sizeable screen of 1920 by 1080 pixels. If you have 30 pixels in width and 30 pixels of height, you can fit 60 * 36, or 2304 images on screen. At some point you will not be able to make out anything.

Multiple canvas elements

There does not seem to be a reason to use multiple canvas elements. If you want a background with hundreds of images, you can redraw the same canvas as you scroll. In fact, this is probably the best way of dealing with your current situation.

Coding style

Wrap your code in an IIFE

Wrap your code in an Immediately Invoked Function Expression. This causes the variables to not spill into other code you or others write. An IIFE looks something like this:

(function () {
  // ...
})();

Look at your variable names

Some of your variable names do not really make sense. cInt seems an iteration of your canvas element. I am not sure why you use loopCInt at all. Replace it with canvasIdentifier or canvasIteration. canvasName is actually a CanvasElement, not a name. goDown looks like a function name at first glance, but is actually the number of pixels from the top, so just name it top. right is actually what you would refer to as left if you worked with absolutely positioned elements, namely the number of pixels from the left edge.

Bookkeeping

You are using a while loop, and because of that have a lot of lines related to bookkeeping. Since you are managing both a row and a column in a single loop, it is not very readable either. I would recommend replacing it with two for loops.

Factor out functions

You want to start factoring out functions with some kind of behaviour earlier than later in your code. You currently have one giant blob of code that, while it does what you want to do, is a nightmare to read through when you expand it. A pretty obvious thing to abstract out is to move things to draw the scene into their own function. You can then call that function to draw or redraw things.

Factor out magic numbers/strings

You currently set "1000" for the width and height, and draw squares of 30 by 30. Move these numbers/strings to their own variable that you can easily change. This allows you to re-use them and quickly change them when you decide, for example, to have a border of 10 pixels around your canvas.

Example

Below I applied some of these to your code. I also put in an example where the canvas is redrawn when you scroll the screen. I added a random number generator that can be seeded, so that I can show off that you can create a consistent screen. You can probably improve on that, considering that it now generates too many colors.

// https://stackoverflow.com/a/19301306/2209007
function RandomGenerator() {
  var m_w = 123456789;
  var m_z = 987654321;
  var mask = 0xffffffff;
  
  // Takes any integer
  this.seed = function(i) {
      m_w = i;
      m_z = 987654321;
  }

  // Returns number between 0 (inclusive) and 1.0 (exclusive),
  // just like Math.random().
  this.random = function()
  {
      m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;
      m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;
      var result = ((m_z << 16) + m_w) & mask;
      result /= 4294967296;
      return result + 0.5;
  }
  
  this.randomHexColor = function()
  {
    var hexFFFFFF = 16777215;
  
    return '#' + Math.floor(this.random() * hexFFFFFF).toString(16);
  }
}

(function() {
  var canvas = document.getElementById('background-canvas');
  var ctx = canvas.getContext("2d");
  var windowWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
  var windowHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
  canvas.setAttribute('width', windowWidth);
  canvas.setAttribute('height', windowHeight);
  
  var imageWidth = 30;
  var imageHeight = 30;
  
  var randomGenerator = new RandomGenerator();
  
  function draw(offset)
  {
    window.requestAnimationFrame(function () {
      randomGenerator.seed(1);

      for (var top = -offset; top < windowHeight; top += imageHeight) {
        for (var left = 0; left < windowWidth; left += imageWidth) {
          if (offset < -imageHeight) {
            randomGenerator.randomHexColor();
            continue;
          }
          ctx.beginPath();
          ctx.rect(left, top, imageWidth, imageHeight);
          ctx.fillStyle = randomGenerator.randomHexColor();
          ctx.fill();
        }
      }
    });
  }

  draw(0);
  
  document.addEventListener('scroll', function() {
    var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
    
    draw(scrollTop);
  });
})();
#background-canvas {
  width: 100vw;
  height: 100vh;
  position: fixed;
  top: 10px;
  left: 10px;
  z-index: -1;
}

body {
  height: 200vh;
}
<canvas id="background-canvas"></canvas>

\$\endgroup\$
8
\$\begingroup\$

Why do you need to display thousands/millions images?

Without knowing that, it's hard to propose a good solution.

  • Perhaps you could just generate that large background once on server side and serve it as a static image?
  • Perhaps you don't need to display them all at once, and could render more of them as the user scrolls down the page?
\$\endgroup\$
2
  • \$\begingroup\$ Or display more pictures as the user zooms in, a la Google maps. \$\endgroup\$ Commented May 6, 2018 at 2:24
  • \$\begingroup\$ [priority.widgetmatic.com/JS_Mosaic2/] or [falconsriseup.com/] These is similar to what i want to achieve, as you can see about 36,000 images without any performance issue, they even boast of more than 5 million pictures. Mine should be easier because my pictures are to be random or in a pattern not necessarily being in a mosaic form. \$\endgroup\$
    – king amada
    Commented May 6, 2018 at 3:55
2
\$\begingroup\$

No way!

A few displays worth no more.

No two devices have the same resources, be that due to hardware limits or current process requirements. You simply can not rely on client devices to give you more than a few screens worth of fast access pixel data. Even a top end machine may have resources tied up in other applications/processes and not be able to handle you page's need for fast random access pixels

Consider the needs.

A million images at medium resolution (2MegPix) will require 1,000,000(Image count) * 2,000,000(pixel count) * 4(pixel channels) = 8e12 bytes (8Tb of RAM)

Nobody is going to download that much information, nor are any consumer level devices able to handle random access to such a large data store in any timely fashion.

The solution

This type of application need to be coupled to a server.

The server holds all the images in a format that allows resolution independent access to the pixels (eg variation of wavelet image compression) the client then requests a view with details about the zoom and the server delivers the images at the resolution required.

At no time does the client need to store more than the display size worth of pixels.

Some examples of this approch are Google Earth / Google Maps and Microsoft's TerraServer

\$\endgroup\$
2
  • \$\begingroup\$ [priority.widgetmatic.com/JS_Mosaic2/] or [falconsriseup.com/] This is similar to what i want to achieve, as you can see about 36,000 images without any performance issue, they even boast of more than 5 million pictures. Mine should be easier because my pictures are to be random in a pattern not necessarily being in a mosaic form. \$\endgroup\$
    – king amada
    Commented May 6, 2018 at 4:00
  • \$\begingroup\$ The first one is showing only has two images, One is the main display the other a tile sheet called avatars.jpg (960by960pixel image) The second is an example of multi resolution image server as described in my answer. \$\endgroup\$
    – Blindman67
    Commented May 6, 2018 at 4:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.