Build an AI‑Powered Draw‑and‑Guess Game with TensorFlow.js on the Frontend

This tutorial walks you through the entire process of creating a browser‑based AI version of the classic "draw‑and‑guess" game, covering model selection, dataset preparation, CNN training with TensorFlow.js, model integration into a Vue front‑end, performance optimizations, and deployment steps.

vivo Internet Technology
vivo Internet Technology
vivo Internet Technology
Build an AI‑Powered Draw‑and‑Guess Game with TensorFlow.js on the Frontend

Introduction

In the AI era, frontend developers can leverage on‑device AI models to create interactive experiences such as an AI‑powered “draw‑and‑guess” game.

AI Quickdraw Gameplay Overview

The game presents a prompt word; the player draws it on a canvas, and an AI model predicts the drawing. If the prediction matches the prompt, the player scores points.

Training the Model

We use the Google QuickDraw dataset (optionally a subset of 80 classes) and TensorFlow.js to build a CNN classifier. The tutorial explains data loading, model definition, training loop, and saving the model files ( model.json, weights.bin, classes.json).

import * as tf from "@tensorflow/tfjs-node";
import { DoodleData } from "./model/doodle-data.model";

export class Classifier {
  constructor(data: DoodleData) {
    this.data = data;
    this.model = tf.sequential();
    this.model.add(tf.layers.conv2d({
      inputShape: [data.IMAGE_WIDTH, data.IMAGE_HEIGHT, 1],
      kernelSize: 3,
      filters: 16,
      strides: 1,
      activation: "relu",
      kernelInitializer: "varianceScaling"
    }));
    this.model.add(tf.layers.maxPooling2d({ poolSize: [2, 2], strides: [2, 2] }));
    this.model.add(tf.layers.conv2d({ kernelSize: 3, filters: 32, strides: 1, activation: "relu", kernelInitializer: "varianceScaling" }));
    this.model.add(tf.layers.maxPooling2d({ poolSize: [2, 2], strides: [2, 2] }));
    this.model.add(tf.layers.flatten());
    this.model.add(tf.layers.dense({ units: data.totalClasses, kernelInitializer: "varianceScaling", activation: "softmax" }));
    const optimizer = tf.train.adam();
    this.model.compile({ optimizer, loss: "categoricalCrossentropy", metrics: ["accuracy"] });
  }

  async train() {
    const trainingData = tf.data.generator(() => this.data.dataGenerator("train")).shuffle(this.data.maxImageClass * this.data.totalClasses).batch(64);
    const testData = tf.data.generator(() => this.data.dataGenerator("test")).shuffle(this.data.maxImageClass * this.data.totalClasses).batch(64);
    await this.model.fitDataset(trainingData, { epochs: 5, validationData: testData });
  }

  async save() {
    fs.mkdirSync("doodle-model", { recursive: true });
    fs.writeFileSync("doodle-model/classes.json", JSON.stringify({ classes: this.data.classes }));
    await this.model.save("file://./doodle-model");
  }
}

Integrating the Model into the Page

After training, the model files are placed in the public assets folder. DoodleClassifier.js loads the model and provides loadModel, predictTopN, and predict methods.

import * as tf from "@tensorflow/tfjs";
import apiClient from "@/services/http";

export default class DoodleClassifier {
  async loadModel() {
    this.model = await tf.loadLayersModel("assets/doodle-model/model.json");
    const response = await apiClient.get("assets/doodle-model/classes.json");
    this.classes = response.data.classes;
  }

  async predictTopN(data, n) {
    const predictions = Array.from(await this.model.predict(data).data());
    const indexed = predictions.map((p, i) => ({ probability: p, index: i }));
    indexed.sort((a, b) => b.probability - a.probability);
    return indexed.slice(0, n).map(p => ({ label: this.classes[p.index], accuracy: p.probability }));
  }

  async predict(data) {
    const argMax = await this.model.predict(data).argMax(-1).data();
    return this.classes[argMax[0]];
  }
}

The Vue component DoodleView.vue creates a canvas, captures its pixel data, normalizes it, and calls the classifier to obtain the top‑N predictions.

const tensor = tf.browser.fromPixels(imgData, 1);
const resized = tf.image.resizeBilinear(tensor, [28, 28])
  .reshape([1, 28, 28, 1])
  .toFloat();
const normalized = tf.scalar(1.0).sub(resized.div(tf.scalar(255.0)));
this.model.predictTopN(normalized, 5).then(predictions => { this.predictions = predictions; });

Optimization Measures

Standardize canvas input by center‑cropping and thickening strokes, and move inference to a Web Worker to keep the UI responsive.

Conclusion

The end‑to‑end example shows how frontend engineers can adopt on‑device AI for low‑latency, privacy‑preserving interactions, while also noting that cloud‑based APIs may be preferable when resources allow.

References

RiccardoGai/doddle-classifier-model

RiccardoGai/doddle-classifier-app

yining1023/doodleNet

image classificationTensorFlow.jsquickdraw
vivo Internet Technology
Written by

vivo Internet Technology

Sharing practical vivo Internet technology insights and salon events, plus the latest industry news and hot conferences.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.