Skip to main content

How it's built

Table of Contents

images.pixels.ovh

Rendering images on-demand consumes significant resources and adds waiting time.

Rendering images on-demand can consume considerable resources and increase loading times.

Uploading a link to a prerendered image, rather than rendering it on-the-fly, can save up to 1 second per image. Under heavy load, this time savings can be even greater. For a game with 4 images per round, prerendering can save as much as 4 seconds in loading time.

Therefore, a simple and effective solution is to prerender images in advance, ensuring faster load times and a smoother experience for players.

Example
images.pixels.ovh/587d7ab468f0b8360dcc10b82035cdf6

Hashing location on bot side

Nodejs
const crypto = require("crypto");

function hashWithSalt(input, salt) {
    const salted = input + salt; // Combine input with salt
    const hash = crypto.createHash("md5").update(salted).digest("hex");
    return hash;
}

const location = "/game/1.jpg";
const salt = "MySuperSecretSalt";
const hash = hashWithSalt(location, salt);
console.log(hash); // Output: 9e15b15ae872f4a61b4d54c11c338344

Assume the file is located at /game/2.jpg. When hashed with MD5, this path results in the hash 587d7ab468f0b8360dcc10b82035cdf6.

Page Logic

PHP
graph TD; A[Request https://images.pixels.ovh/587d7ab468f0b8360dcc10b82035cdf6] --> B[/Check if string has 32 characters containing only a-f and 0-9/] B -->|Invalid Hash Format| G[Return image from not_found.jpg] B -->|Valid Hash Format| C[/Check if Hash list exists for current version/] C --> |False| Y[MD5 Hash All File Locations] Y --> Z[Store Hashes in Database] Z --> X C --> |True| X[/Search for Hash in Database/] X -->|Found| D[Return image from /game/2.jpg] X -->|Not Found| F[Return image from not_found.jpg]

How bot knows locations?

PHP and Nodejs During bot startup an API provides the bot with the complete site structure, allowing it to replicate this structure locally with placeholder files, saving space and bandwidth.

Pros and cons of that approach

Pros:

  • Eliminates additional loading times, providing a smoother user experience.
  • Hashing helps obfuscate URLs, making it harder for users to access images directly.
  • Adding a salt to the hash provides an extra layer of security against URL manipulation.

Cons:

  • Hash collisions, although rare, could occur, potentially leading to duplicate URLs for different images.
  • Images remain accessible via URL, which might allow determined users to bypass game mechanics.
  • Some users may save URLs to predict game answers, which could affect the guessing challenge.

Rendering Images

To optimize storage, I compress all images and convert them to .jpg format, reducing file size without significant loss of quality. For image editing, I use imageMagick alongside the npm package seedrandom. to randomize image variations.

Reducing Image Repetition for Each Game

To keep gameplay fresh and minimize repetition, I create multiple image variations per game. I use 5 unique random strings as seeds (e.g., 93JlS) to generate variations. With 5 base images for each game, this approach results in a total of 25 possible starting images, giving players a more varied experience.

Classic mode


JSON
"Classic": {
			"MaxLives": 4,
			"ImageVariables": 5,
			"BlurInRounds":{
				"1":98,
				"2":80,
				"3":60,
				"4":20
			}
		},

Nodejs

graph TD; A[Compress an image] A --> B[Use a 10x10 grid to calculate the average color of each tile and save it to a cache file] subgraph One-time A B end
graph TD; C[Load the compressed image] C --> D[Generate an array of blocks to blur using seeded randomization] D --> E[For each tile to be blurred, load RGB data from the average color and fill the tile with it] E --> F[Save as round_number.jpg] subgraph Every image C D E F end

Puzzle mode

Zoom mode