Handwritten Digit Playground Notes: Bringing an Offline Classifier into the Browser
Handwritten Digit Playground Notes: Bringing an Offline Classifier into the Browser
Search
Ask the AI

Handwritten Digit Playground Notes: Bringing an Offline Classifier into the Browser

After adding the handwritten digit project to the site, I did not want the playground to retrain all 42000 samples inside the browser. That would be too heavy, especially on phones. The current setup is more practical: keep the full offline C project as a download, and add a lightweight interactive browser model for immediate experimentation.

That split keeps the system honest. Visitors do not need a local compiler just to understand the input and prediction flow, and the offline project can stay focused on the actual implementation instead of being distorted to fit a front-end demo.

1. What the handwritten digit module does

The new playground tab supports three main actions:

  • Browse real samples loaded from the training set into a 28 by 28 grid
  • Draw your own input directly in the browser and run a prediction
  • Inspect class probabilities for all ten digits instead of only showing one final label

This is useful for learning because you can see not only what the model predicts, but also which competing digits remain plausible.

2. How the browser model relates to the C project

The page does not load the full training loop. It loads a compact set of pre-trained softmax weights. That keeps the browser demo in the same model family as the C project while making the interaction fast enough for normal page use.

The current browser demo weights come from a smaller training split:

  • training samples: 10000
  • validation samples: 2000
  • epochs: 18
  • learning rate: 0.35
  • validation accuracy: about 90.65%

The goal is not to chase the strongest possible score. The goal is to make the model response immediate and interpretable in the browser while preserving the full downloadable project separately.

Why the digits you draw score well below 90%

That 90.65% is accuracy on the validation set. Draw a few digits in the browser and the error rate is visibly higher — with nothing wrong with the model itself.

The reason is that the training data and your input are not the same distribution. MNIST samples were not written casually; they went through fixed preprocessing. Scanned forms were binarised, then scaled by the digit’s bounding box to fit a 20×20 area, placed on a 28×28 canvas, and translated so the pixel centre of mass sits at the centre. Stroke weight, size and position are normalised across every sample.

The digit you draw with a mouse or finger has none of that: the stroke may be very thick or very thin, it may sit in one corner, it may occupy a third of the canvas. None of this matters to a human eye, and all of it is fatal to a linear softmax model — what it learned is “which pixel positions light up,” so shifting the input by a few pixels activates an entirely different set of weights.

The correct remedy is not retraining but applying the same preprocessing at inference time:

// 1. find the stroke's bounding box, crop the surrounding blank
// 2. scale proportionally so the longest side is 20 pixels
// 3. place it on a 28x28 canvas
// 4. translate so the pixel centre of mass is centred
function centerByMass(img, w, h) {
  let sx = 0, sy = 0, total = 0;
  for (let y = 0; y < h; y++)
    for (let x = 0; x < w; x++) {
      const v = img[y * w + x];
      sx += x * v; sy += y * v; total += v;
    }
  return { dx: w / 2 - sx / total, dy: h / 2 - sy / total };
}

With those steps in place, the same model performs markedly better on hand-drawn input — the gain comes from aligning distributions, not from a stronger model.

There is a lesson here more general than handwritten digits: an accuracy figure holds only on the distribution of the validation set that produced it. Change the input distribution at deployment and that number stops being valid — and it does not invalidate itself; the model goes on producing confident predictions. I hit a worse version of this in How I Fooled Myself Validating Quantisation: validating only on samples the model was good at reported IoU 0.98, and a different class of real images dropped it to 0.30.

So on this playground, the probability distribution carries more information than the final answer. If all ten classes have roughly equal probability, the input has landed in a region the model has never seen, and whichever class holds the highest probability there is essentially arbitrary.

3. Why the browser does not retrain the whole project

The reasons are straightforward:

  • the full dataset is too large to reprocess on every page load
  • repeated gradient updates in the browser would hurt interaction quality
  • mobile users need a stable experience more than a heavy in-page training loop

So the playground is better used as a visualization and interaction surface, not as a replacement for the offline training environment.

4. The best way to use the module

A good sequence is:

  1. load a real sample and confirm that the grid and prediction agree
  2. edit a few strokes and watch the probability distribution change
  3. clear the board and draw a digit yourself to see where the model hesitates

If you draw a 9 and the model also gives meaningful probability to 4 or 7, that is not a failure of the UI. It is a useful view into the model boundary.

5. How it fits with the posts and downloads

The handwritten digit content is now structured as one connected set:

  • post one explains the dataset files and the 784-dimensional input format
  • post two explains the C softmax classifier, training loop, and exported predictions
  • the downloads page collects the source, compressed datasets, sample submission, final submission, and browser model file
  • the playground provides the interactive lightweight version

That way, readers can understand the logic in the posts, grab the files from downloads, and try the behavior immediately in the playground.

6. What is worth improving next

This version is already useful for teaching, but there are clear next steps:

  • add input helpers such as brush-size controls or centering logic
  • collect misclassified examples into a browsable error set
  • compare the browser demo outputs against the offline C project in more detail

For a learning-oriented technical site, those improvements matter more than extra decoration. The point is to make the relationship between input, model, and output easier to study.

7. Browser Playground Validation Table

The browser playground is useful only if it stays honest about what it is demonstrating. The table below separates UI behavior, model behavior, and offline project behavior so the page remains an experiment surface rather than a black-box toy.

Check Evidence to inspect Why it matters Boundary
Input grid Drawn strokes and sample images map to a 28 by 28 numeric grid. The browser UI should match the dataset representation. Canvas smoothing or centering can change the model input.
Probability output All ten class scores are visible and sum to a probability distribution. Seeing the runner-up classes teaches uncertainty better than one label. High probability is not proof that the model is correct.
Offline alignment The page links back to dataset, C classifier, and downloads. Readers can trace the demo back to reproducible source files. The browser model is lightweight and does not replace full training.
Error exploration Ambiguous digits reveal competing probabilities such as 4/7/9. Misclassification examples show model boundaries. The UI should not hide uncertainty behind decorative feedback.

8. Where to continue

If you have not read the implementation post yet, continue with the C classifier article. If you want to run the full project locally, go straight to the downloads page. If you just want to experiment right away, open the playground and start drawing digits.

Leave a Reply

Scroll down