Commercial HTML5 math game “DrawSum” source code available for free

Three years ago I deleveloped a math game called DrawSum for a learning project.

It was a mix between a draw and match game based upon Dungeon Raid engine and a pure math game.

I used Phaser 2.4.8, but it’s perfectly compatible with latest Phaser CE release and you can easily port to Phaser 3 or any other language if you prefer (I am about do it).

Have a look at the game:

As the title suggests, you have to draw to connect tiles and match the numbers above before time runs out.

Why am I releasing a code which is more than three years old?

First, because source code never gets old. Sure, time passes and frameworks update and get more and more powerful, but it’s easy to adapt the scripts to new – if any – methods and properties.

Second, because even if – but it’s not – but even if the script is a bit obsolete, most algorithm are evergreen. An array in early 1980 remains an array in 2020 and if the algorithm says you have to iterate through an array, you have to do it, no matter if we are in 1980 or in 2020, just do it in the most comfortable or modern way you prefer.

And this game features a lot of interesting snippets, such as the one to increase difficulty as players keep matching, or the one to suggest the first move. Or the backtracking! What about move backtracking? Saving high scores? Ok, I am sure you got the point.

Less than 500 lines for a complete game:

var game;
var savedData;
var score;
var gameOptions = {
    bgColors: [0x42a7bd, 0xd45477],
    gameWidth: 750,
    gameHeight: 1334,
    tileSize: 140,
    fieldSize: {
        rows: 5,
        cols: 5
    },
    fallSpeed: 250,
    localStorageName: "drawsumgame"
}
window.onload = function() {
    var windowRatio = window.innerWidth / window.innerHeight;
    if(windowRatio < gameOptions.gameWidth / gameOptions.gameHeight){
        gameOptions.gameHeight = gameOptions.gameWidth / windowRatio;
    }
    game = new Phaser.Game(gameOptions.gameWidth, gameOptions.gameHeight);
    game.state.add("Boot", boot);
    game.state.add("Preload", preload);
    game.state.add("TitleScreen", titleScreen);
    game.state.add("TheGame", theGame);
    game.state.add("GameOver", gameOver);
    game.state.start("Boot");
}
var boot = function(game){};
boot.prototype = {
    preload: function(){
        game.scale.pageAlignHorizontally = true;
        game.scale.pageAlignVertically = true;
        game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
        game.stage.disableVisibilityChange = true;
        game.stage.backgroundColor = 0x26292c;
        this.game.load.image("loading","assets/sprites/loading.png");
    },
  	create: function(){
        game.plugin = game.plugins.add(Phaser.Plugin.FadePlugin);
		game.state.start("Preload");
    }
}
var preload = function(game){};
preload.prototype = {
    preload: function(){
        var loadingBar = this.add.sprite(game.width / 2, game.height / 2, "loading");
        loadingBar.anchor.setTo(0.5);
        game.load.image("playbutton", "assets/sprites/playbutton.png");
        game.load.image("hand", "assets/sprites/hand.png");
        game.load.image("bigtile", "assets/sprites/bigtile.png");
        game.load.image("title", "assets/sprites/title.png");
        game.load.image("item", "assets/sprites/item.png");
        game.load.spritesheet("tiles", "assets/sprites/tiles.png", gameOptions.tileSize, gameOptions.tileSize);
        game.load.spritesheet("arrows", "assets/sprites/arrows.png", gameOptions.tileSize * 3, gameOptions.tileSize * 3);
        game.load.spritesheet("numbers", "assets/sprites/numbers.png", gameOptions.tileSize, gameOptions.tileSize);
        game.load.bitmapFont("bignumbersfont", "assets/fonts/bignumbersfont.png", "assets/fonts/bignumbersfont.fnt");
        game.load.bitmapFont("recapfont", "assets/fonts/recapfont.png", "assets/fonts/recapfont.fnt");
        game.load.bitmapFont("font", "assets/fonts/font.png", "assets/fonts/font.fnt");
        game.load.audio("pop", ["assets/sounds/pop.mp3", "assets/sounds/pop.ogg"]);
        game.load.audio("pop2", ["assets/sounds/pop2.mp3", "assets/sounds/pop2.ogg"]);
        game.load.audio("pop3", ["assets/sounds/pop3.mp3", "assets/sounds/pop3.ogg"]);
        game.load.audio("fail", ["assets/sounds/fail.mp3", "assets/sounds/fail.ogg"]);
        game.load.audio("done", ["assets/sounds/done.mp3", "assets/sounds/done.ogg"]);
        game.load.audio("gameover", ["assets/sounds/gameover.mp3", "assets/sounds/gameover.ogg"]);
    },
    create: function(){
        game.plugin.fadeAndPlay("rgb(38, 41, 44)", 0.25, "TitleScreen");
    }
}
var titleScreen = function(game){};
titleScreen.prototype = {
    create: function(){
        savedData = localStorage.getItem(gameOptions.localStorageName)==null?{score:0}:JSON.parse(localStorage.getItem(gameOptions.localStorageName));
        var title = game.add.image(game.width / 2, 20, "title");
        title.anchor.set(0.5, 0);
        var playButton = game.add.button(game.width / 2, game.height / 2, "playbutton", this.startGame);
        playButton.anchor.set(0.5);
        var tween = game.add.tween(playButton).to({
            width: 220,
            height:220
        }, 1500, "Linear", true, 0, -1, true);
        game.add.bitmapText(game.width / 2, game.height - 200, "font", "BEST SCORE", 48).anchor.set(0.5);
        game.add.bitmapText(game.width / 2, game.height - 100, "bignumbersfont", savedData.score.toString(), 90).anchor.set(0.5);
    },
    startGame: function(){
        game.plugin.fadeAndPlay("rgb(38, 41, 44)", 0.25, "TheGame");
    }
}
var theGame = function(){};
theGame.prototype = {
  	create: function(){
        this.createLevel();
        game.input.onDown.add(this.pickTile, this);
        this.popSound = [game.add.audio("pop"), game.add.audio("pop2"), game.add.audio("pop3")];
        this.failSound = game.add.audio("fail");
        this.doneSound = game.add.audio("done");
        this.gameOverSound = game.add.audio("gameover");
  	},
	createLevel: function(){
        game.stage.visible = false;
        this.tilesArray = [];
        this.arrowsArray = [];
        this.targetsArray = [];
        this.matches = 0;
        this.energyLoss = 0.3;
        this.gameOver = false;
        score = 0;
        this.firstPick = true;
        this.tintColor = gameOptions.bgColors[0];
        this.altTintColor = gameOptions.bgColors[1];
        this.tileGroup = game.add.group();
        this.tileGroup.x = (game.width - gameOptions.tileSize * gameOptions.fieldSize.cols) / 2;
        this.tileGroup.y = (game.height -  gameOptions.tileSize * gameOptions.fieldSize.rows) - 50;
        this.arrowsGroup = game.add.group();
        this.arrowsGroup.x = this.tileGroup.x;
        this.arrowsGroup.y = this.tileGroup.y;
        var item = game.add.image(game.width / 2, this.tileGroup.y - 70 , "item");
        item.anchor.set(0.5);
        item.tint = this.tintColor;
        this.recapText = game.add.bitmapText(40, item.y - 25, "recapfont", "", 72);
        this.scoreText = game.add.bitmapText(game.width - 40, item.y - 100, "font", "", 48);
        this.scoreText.anchor.set(1, 0);
        this.targetGroup = game.add.group();
        this.arcGraphics = game.add.graphics(0, 0);
        tileMask = game.add.graphics(this.tileGroup.x, this.tileGroup.y - 40);
        tileMask.beginFill(0xffffff);
        tileMask.drawRect(0, 0, gameOptions.tileSize * gameOptions.fieldSize.cols, gameOptions.tileSize * gameOptions.fieldSize.rows + 40);
        this.tileGroup.mask = tileMask;
  		for(var i = 0; i < gameOptions.fieldSize.rows; i++){
            this.tilesArray[i] = [];
			for(var j = 0; j < gameOptions.fieldSize.cols; j++){
			             this.addTile(i, j);
			}
		}
        this.removedTiles = [];
        for(i = 0; i < 5; i++){
            var target = game.add.sprite((i % 3) * 230 + 115 * Math.floor(i / 3), 15 + Math.floor(i / 3) * 200, "bigtile");
            target.tint = this.altTintColor;
            target.numberToMatch = game.rnd.between(10, 14);
            target.energy = 360;
            target.energyLoss = this.energyLoss
            var bigNumber = game.add.bitmapText(100, 110, "bignumbersfont", target.numberToMatch.toString(), 90);
            bigNumber.anchor.set(0.5);
            target.addChild(bigNumber)
            this.targetGroup.add(target);
            this.targetsArray.push(target)
            this.arcGraphics.arc(this.targetsArray[i].x + 100 + this.targetGroup.x, this.targetsArray[i].y + 100, 80, 0, Phaser.Math.degToRad(this.targetsArray[i].energy), false);
        }
        this.targetGroup.x = (game.width - this.targetGroup.width) / 2
        this.timeLoop = game.time.events.loop(Phaser.Timer.SECOND / 20, this.updateCounter, this);
        var tweenArray = this.findSum(this.targetsArray[0].numberToMatch);
        if(tweenArray.length == 0){
            game.state.start("TheGame");
            return;
        }
        game.stage.visible = true;
        this.finger = game.add.sprite(this.tilesArray[tweenArray[0]][tweenArray[1]].x - 80, this.tilesArray[tweenArray[0]][tweenArray[1]].y, "hand");
        this.tileGroup.add(this.finger);
        game.add.tween(this.finger).to({
            x: this.tilesArray[tweenArray[2]][tweenArray[3]].x - 80,
            y: this.tilesArray[tweenArray[2]][tweenArray[3]].y
        }, 500, Phaser.Easing.Linear.None, true, 0, -1, true);
        this.infoText = game.add.bitmapText(game.width / 2, item.y, "font", "Connect blue numbers to sum them and\nmatch red numbers before time runs out\n\nLonger connections give more points", 24)
        this.infoText.anchor.set(0.5);
	},
    updateCounter: function(){
        this.arcGraphics.clear();
        this.arcGraphics.lineStyle(20, 0xffffff);
        for(var i = 0; i < this.targetsArray.length; i++){
            this.targetsArray[i].energy -= this.targetsArray[i].energyLoss;
            if(this.targetsArray[i].energy > 0){
                this.arcGraphics.arc(this.targetsArray[i].x + 100 + this.targetGroup.x, this.targetsArray[i].y + 100, 80, 0, Phaser.Math.degToRad(this.targetsArray[i].energy), false);
            }
            else{
                game.time.events.remove(this.timeLoop);
                this.gameOver = true;
                game.add.tween(this.targetsArray[i]).to({
                    y: game.height + 200
                }, 500, Phaser.Easing.Cubic.In, true);
            }
        }
        if(this.gameOver){
            this.gameOverSound.play();
            game.time.events.loop(Phaser.Timer.SECOND * 2, function(){
                game.plugin.fadeAndPlay("rgb(38, 41, 44)", 0.25, "GameOver");
            }, this);
        }
    },
	addTile: function(row, col){
        var tileXPos = col * gameOptions.tileSize + gameOptions.tileSize / 2;
        var tileYPos = row * gameOptions.tileSize + gameOptions.tileSize / 2;
        var theTile = game.add.sprite(tileXPos, tileYPos, "tiles");
        theTile.anchor.set(0.5);
        theTile.picked = false;
        theTile.coordinate = new Phaser.Point(col, row);
        this.tilesArray[row][col] = theTile;
        theTile.value = game.rnd.between(1, 9);
        theTile.tint = this.tintColor;
        var number = game.add.sprite(0, 0, "numbers");
        number.anchor.set(0.5);
        number.frame = theTile.value - 1;
        theTile.addChild(number);
        this.tileGroup.add(theTile);
	},
    pickTile: function(e){
        if(this.firstPick){
            this.infoText.destroy();
            this.finger.destroy();
            this.firstPick = false;
        }
        this.visitedTiles = [];
        this.visitedTiles.length = 0;
        if(this.tileGroup.getBounds().contains(e.position.x, e.position.y)){
            var col = Math.floor((e.position.x - this.tileGroup.x) / gameOptions.tileSize);
            var row = Math.floor((e.position.y - this.tileGroup.y) / gameOptions.tileSize);
            this.tilesArray[row][col].tint = this.altTintColor;
            this.tilesArray[row][col].picked = true;
            game.input.onDown.remove(this.pickTile, this);
            game.input.onUp.add(this.releaseTile, this);
            game.input.addMoveCallback(this.moveTile, this);
            this.visitedTiles.push(this.tilesArray[row][col].coordinate);
            this.recapText.text = this.tilesArray[row][col].value;
            Phaser.ArrayUtils.getRandomItem(this.popSound).play();
        }
    },
    moveTile: function(e){
        if(this.tileGroup.getBounds().contains(e.position.x, e.position.y)){
            var col = Math.floor((e.position.x - this.tileGroup.x) / gameOptions.tileSize);
            var row = Math.floor((e.position.y - this.tileGroup.y) / gameOptions.tileSize);
            var distance = new Phaser.Point(e.position.x - this.tileGroup.x, e.position.y - this.tileGroup.y).distance(this.tilesArray[row][col]);
            if(distance < gameOptions.tileSize * 0.4){
                if(!this.tilesArray[row][col].picked && this.checkAdjacent(new Phaser.Point(col, row), this.visitedTiles[this.visitedTiles.length - 1])){
                    if(this.visitedTiles.length < 8){
                        this.tilesArray[row][col].picked = true;
                        this.tilesArray[row][col].tint = this.altTintColor;
                        this.visitedTiles.push(this.tilesArray[row][col].coordinate);
                        this.addArrow();
                        Phaser.ArrayUtils.getRandomItem(this.popSound).play();
                    }
                }
                else{
                    if(this.visitedTiles.length > 1 && row == this.visitedTiles[this.visitedTiles.length - 2].y && col == this.visitedTiles[this.visitedTiles.length - 2].x){
                        this.tilesArray[this.visitedTiles[this.visitedTiles.length - 1].y][this.visitedTiles[this.visitedTiles.length - 1].x].picked = false;
                        this.tilesArray[this.visitedTiles[this.visitedTiles.length - 1].y][this.visitedTiles[this.visitedTiles.length - 1].x].tint = this.tintColor;
                        this.visitedTiles.pop();
                        this.arrowsArray[this.arrowsArray.length - 1].destroy();
                        this.arrowsArray.pop();
                        Phaser.ArrayUtils.getRandomItem(this.popSound).play();
                    }
                }
                var stringToShow = this.tilesArray[this.visitedTiles[0].y][this.visitedTiles[0].x].value;
                for(var i = 1; i < this.visitedTiles.length; i++){
                    stringToShow += "+" + this.tilesArray[this.visitedTiles[i].y][this.visitedTiles[i].x].value
                }
                this.recapText.text = stringToShow
            }
        }
    },
    releaseTile: function(){
        this.recapText.text = "";
        var didMatch = false;
        var totalSum = 0;
        for(var i = 0; i < this.visitedTiles.length; i++){
            totalSum += this.tilesArray[this.visitedTiles[i].y][this.visitedTiles[i].x].value;
        }
        if(!this.gameOver){
            for(i = 0; i < 5; i++){
                if(totalSum == this.targetsArray[i].numberToMatch){
                    this.matches++;
                    var tween = game.add.tween(this.targetsArray[i]).to({
                        alpha: 0
                    }, gameOptions.fallSpeed / 2, Phaser.Easing.Linear.None, true);
                    this.targetsArray[i].numberToMatch = game.rnd.between(10, 14 + this.matches);
                    tween.onComplete.add(function(e){
                        e.children[0].text = e.numberToMatch;
                        var tween = game.add.tween(e).to({
                            alpha: 1
                        }, gameOptions.fallSpeed / 2, Phaser.Easing.Linear.None, true);
                    }, this);
                    this.targetsArray[i].energy = 360;
                    this.targetsArray[i].energyLoss = this.energyLoss;
                    didMatch = true;
                    score += totalSum * (this.visitedTiles.length - 1);
                    this.scoreText.text = score.toString();
                }
            }
        }
        game.input.onUp.remove(this.releaseTile, this);
        game.input.deleteMoveCallback(this.moveTile, this);
        this.arrowsGroup.removeAll(true);
        if(didMatch){
            this.doneSound.play();
            this.energyLoss += 0.02;
            this.clearPath();
            this.tilesFallDown();
            this.placeNewTiles();
        }
        else{
            for(var i = 0; i < this.visitedTiles.length; i++){
                this.tilesArray[this.visitedTiles[i].y][this.visitedTiles[i].x].tint = this.tintColor;
                this.tilesArray[this.visitedTiles[i].y][this.visitedTiles[i].x].picked = false;
            }
            this.failSound.play()
            this.nextPick();
        }
    },
    checkAdjacent: function(p1, p2){
        return (Math.abs(p1.x - p2.x) <= 1) && (Math.abs(p1.y - p2.y) <= 1);
    },
    addArrow: function(){
        var fromTile = this.visitedTiles[this.visitedTiles.length - 2];
        var arrow = game.add.sprite(this.tilesArray[fromTile.y][fromTile.x].x, this.tilesArray[fromTile.y][fromTile.x].y, "arrows");
        arrow.tint = this.tintColor;
        this.arrowsGroup.add(arrow);
        arrow.anchor.set(0.5);
        var tileDiff = new Phaser.Point(this.visitedTiles[this.visitedTiles.length - 1].x, this.visitedTiles[this.visitedTiles.length - 1].y)
        tileDiff.subtract(this.visitedTiles[this.visitedTiles.length - 2].x, this.visitedTiles[this.visitedTiles.length - 2].y);
        if(tileDiff.x == 0){
            arrow.angle = -90 * tileDiff.y;
        }
        else{
            arrow.angle = 90 * (tileDiff.x + 1);
            if(tileDiff.y != 0){
                arrow.frame = 1;
                if(tileDiff.y + tileDiff.x == 0){
                    arrow.angle -= 90;
                }
            }
        }
        this.arrowsArray.push(arrow);
    },
    clearPath: function(){
        for(var i = 0; i < this.visitedTiles.length; i++){
            this.tilesArray[this.visitedTiles[i].y][this.visitedTiles[i].x].visible = false;
            this.removedTiles.push(this.tilesArray[this.visitedTiles[i].y][this.visitedTiles[i].x]);
            this.tilesArray[this.visitedTiles[i].y][this.visitedTiles[i].x] = null;
        }
    },
    tilesFallDown: function(){
        for(var i = gameOptions.fieldSize.cols - 1; i >= 0; i--){
            for(var j = 0; j < gameOptions.fieldSize.rows; j++){
                if(this.tilesArray[i][j] != null){
                    var holes = this.holesBelow(i, j);
                    if(holes > 0){
                        var coordinate = new Phaser.Point(this.tilesArray[i][j].coordinate.x, this.tilesArray[i][j].coordinate.y);
                        var destination = new Phaser.Point(j, i + holes);
                        var tween = game.add.tween(this.tilesArray[i][j]).to({
                            y: this.tilesArray[i][j].y + holes * gameOptions.tileSize
                        }, gameOptions.fallSpeed, Phaser.Easing.Linear.None, true);
                        tween.onComplete.add(this.nextPick, this)
                        this.tilesArray[destination.y][destination.x] = this.tilesArray[i][j]
                        this.tilesArray[coordinate.y][coordinate.x] = null;
                        this.tilesArray[destination.y][destination.x].coordinate = new Phaser.Point(destination.x, destination.y)
                        this.tilesArray[destination.y][destination.x].children[0].text = "R" + destination.y + ", C" + destination.x;
                    }
                }
            }
        }
    },
    placeNewTiles: function(){
        for(var i = 0; i < gameOptions.fieldSize.cols; i++){
            var holes = this.holesInCol(i);
            if(holes > 0){
                for(var j = 1; j <= holes; j++){
                    var tileXPos = i * gameOptions.tileSize + gameOptions.tileSize / 2;
                    var tileYPos = -j * gameOptions.tileSize + gameOptions.tileSize / 2;
                    var theTile = this.removedTiles.pop();
                    theTile.position = new Phaser.Point(tileXPos, tileYPos);
                    theTile.visible = true;
                    theTile.tint = this.tintColor;
                    theTile.picked = false;
                    var tween = game.add.tween(theTile).to({
                        y: theTile.y + holes * gameOptions.tileSize
                    }, gameOptions.fallSpeed, Phaser.Easing.Linear.None, true)
                    tween.onComplete.add(this.nextPick, this)
                    theTile.coordinate = new Phaser.Point(i, holes - j);
                    theTile.value = game.rnd.between(1, 9);
                    theTile.children[0].frame = theTile.value - 1;
                    this.tilesArray[holes - j][i] = theTile;
                }
            }
        }
    },
    nextPick: function(){
        if(!game.input.onDown.has(this.pickTile, this)){
            game.input.onDown.add(this.pickTile, this);
        }
    },
    holesBelow: function(row, col){
        var result = 0;
        for(var i = row + 1; i < gameOptions.fieldSize.rows; i++){
            if(this.tilesArray[i][col] == null){
                result ++;
            }
        }
        return result;
    },
    holesInCol: function(col){
        var result = 0;
        for(var i = 0; i < gameOptions.fieldSize.rows; i++){
            if(this.tilesArray[i][col] == null){
                result ++;
            }
        }
        return result;
    },
    findSum: function(n){
        for(var i = 1; i < gameOptions.fieldSize.rows - 3; i++){
            for(var j = 1; j < gameOptions.fieldSize.cols - 2; j++){
                for(var k = 0; k <= 1; k++){
                    for(var l = 0; l <= 1; l++){
                        var newRow = i + k;
                        var newCol = j + l;
                        if((k != 0 || l != 0) && newRow < gameOptions.fieldSize.rows && newRow >= 0 && newCol < gameOptions.fieldSize.cols && newCol >= 0){
                            if(this.tilesArray[i][j].value + this.tilesArray[newRow][newCol].value == n){
                                return([i, j, newRow, newCol]);
                            }
                        }
                    }
                }
            }
        }
        return [];
    }
}
var gameOver = function(){};
gameOver.prototype = {
    create: function(){
        var bestScore = Math.max(score, savedData.score);
        game.add.bitmapText(game.width / 2, 100, "font", "Your score", 48).anchor.set(0.5);
        game.add.bitmapText(game.width / 2, 200, "bignumbersfont", score.toString(), 90).anchor.set(0.5);
        game.add.bitmapText(game.width / 2, game.height - 200, "font", "Best score", 48).anchor.set(0.5);
        game.add.bitmapText(game.width / 2, game.height - 100, "bignumbersfont", bestScore.toString(), 90).anchor.set(0.5);
        localStorage.setItem(gameOptions.localStorageName,JSON.stringify({
            score: bestScore
     	}));
        var playButton = game.add.button(game.width / 2, game.height /2, "playbutton", this.startGame);
        playButton.anchor.set(0.5);
        var tween = game.add.tween(playButton).to({
            width: 220,
            height:220
        }, 1500, "Linear", true, 0, -1, true);
    },
    startGame: function(){
        game.plugin.fadeAndPlay("rgb(38, 41, 44)", 0.25, "TheGame");
    }
}
Phaser.Plugin.FadePlugin = function (game, parent) {
	Phaser.Plugin.call(this, game, parent);
};
Phaser.Plugin.FadePlugin.prototype = Object.create(Phaser.Plugin.prototype);
Phaser.Plugin.FadePlugin.prototype.constructor = Phaser.Plugin.SamplePlugin;
Phaser.Plugin.FadePlugin.prototype.fadeAndPlay = function (style, time, nextState) {
    this.crossFadeBitmap = this.game.make.bitmapData(this.game.width, this.game.height);
	this.crossFadeBitmap.rect(0, 0, this.game.width, this.game.height, style);
	this.overlay = this.game.add.sprite(0, 0, this.crossFadeBitmap);
	this.overlay.alpha = 0;
	var fadeTween = this.game.add.tween(this.overlay);
	fadeTween.to({
        alpha:1
    },
    time * 1000, Phaser.Easing.None, true);
    fadeTween.onComplete.add(function(){
        this.game.state.start(nextState);
    }, this);
};

I will release a Phaser 3 commented version really soon – it’s part of another project – meanwhile download the source code of this game, have fun and if you create something interesting out of it, just let me know.

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

215 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 1+2=3
// 100 rounds
// 10000000
// 2 Cars
// 2048
// A Blocky Christmas
// A Jumping Block
// A Life of Logic
// Angry Birds
// Angry Birds Space
// Artillery
// Astro-PANIC!
// Avoider
// Back to Square One
// Ball Game
// Ball vs Ball
// Ball: Revamped
// Balloon Invasion
// BallPusher
// Ballz
// Bar Balance
// Bejeweled
// Biggification
// Block it
// Blockage
// Bloons
// Boids
// Bombuzal
// Boom Dots
// Bouncing Ball
// Bouncing Ball 2
// Bouncy Light
// BoxHead
// Breakout
// Bricks
// Bubble Chaos
// Bubbles 2
// Card Game
// Castle Ramble
// Chronotron
// Circle Chain
// Circle Path
// Circle Race
// Circular endless runner
// Cirplosion
// CLOCKS - The Game
// Color Hit
// Color Jump
// ColorFill
// Columns
// Concentration
// Crossy Road
// Crush the Castle
// Cube Jump
// CubesOut
// Dash N Blast
// Dashy Panda
// Deflection
// Diamond Digger Saga
// Don't touch the spikes
// Dots
// Down The Mountain
// Drag and Match
// Draw Game
// Drop Wizard
// DROP'd
// Dudeski
// Dungeon Raid
// Educational Game
// Elasticity
// Endless Runner
// Erase Box
// Eskiv
// Farm Heroes Saga
// Filler
// Flappy Bird
// Fling
// Flipping Legend
// Floaty Light
// Fuse Ballz
// GearTaker
// Gem Sweeper
// Globe
// Goat Rider
// Gold Miner
// Grindstone
// GuessNext
// Helicopter
// Hero Emblems
// Hero Slide
// Hexagonal Tiles
// HookPod
// Hop Hop Hop Underwater
// Horizontal Endless Runner
// Hundreds
// Hungry Hero
// Hurry it's Christmas
// InkTd
// Iromeku
// Jet Set Willy
// Jigsaw Game
// Knife Hit
// Knightfall
// Legends of Runeterra
// Lep's World
// Line Rider
// Lumines
// Magick
// MagOrMin
// Mass Attack
// Math Game
// Maze
// Meeblings
// Memdot
// Metro Siberia Underground
// Mike Dangers
// Mikey Hooks
// Nano War
// Nodes
// o:anquan
// One Button Game
// One Tap RPG
// Ononmin
// Pacco
// Perfect Square!
// Perfectionism
// Phyballs
// Pixel Purge
// PixelField
// Planet Revenge
// Plants Vs Zombies
// Platform
// Platform game
// Plus+Plus
// Pocket Snap
// Poker
// Pool
// Pop the Lock
// Pop to Save
// Poux
// Pudi
// Pumpkin Story
// Puppet Bird
// Pyramids of Ra
// qomp
// Quick Switch
// Racing
// Radical
// Rebuild Chile
// Renju
// Rise Above
// Risky Road
// Roguelike
// Roly Poly
// Run Around
// Rush Hour
// SameGame
// SamePhysics
// Save the Totem
// Security
// Serious Scramblers
// Shrink it
// Sling
// Slingy
// Snowflakes
// Sokoban
// Space Checkers
// Space is Key
// Spellfall
// Spinny Gun
// Splitter
// Spring Ninja
// Sproing
// Stabilize!
// Stack
// Stairs
// Stick Hero
// String Avoider
// Stringy
// Sudoku
// Super Mario Bros
// Surfingers
// Survival Horror
// Talesworth Adventure
// Tetris
// The Impossible Line
// The Moops - Combos of Joy
// The Next Arrow
// Threes
// Tic Tac Toe
// Timberman
// Tiny Wings
// Tipsy Tower
// Toony
// Totem Destroyer
// Tower Defense
// Trick Shot
// Tunnelball
// Turn
// Turnellio
// TwinSpin
// vvvvvv
// Warp Shift
// Way of an Idea
// Whack a Creep
// Wheel of Fortune
// Where's my Water
// Wish Upon a Star
// Word Game
// Wordle
// Worms
// Yanga
// Yeah Bunny
// Zhed
// zNumbers