Disclaimer: this is exclusively vibe-coded and is entirely GPT-5.5's work. The README is GPT-5.5 written.
Realtime MNIST image generation using the Qwen3-2M MNIST checkpoint and the optimized C decoder exposed through a small Python wrapper.
Use the same conda environment as the training/benchmarking work:
cd /Users/natebreslow/Documents/mnistRealtime
source ~/miniconda3/etc/profile.d/conda.sh
conda activate mlx-experiment
python -m pip install -r requirements.txt
makemake builds libqwen3_mnist_realtime.dylib from:
qwen3_cpu_bench.c: optimized Qwen3/MNIST CPU decoderqwen3_mnist_realtime.c: exported C API shim used by Python
The default model is Qwen3-2M-MNIST-GRPO.raw.
Python bindings for the C generator.
from mnist_realtime import MnistGenerator
with MnistGenerator(seed=1234, temperature=0.8, threads=5) as gen:
image = gen.generate(7) # numpy uint8 array, shape (28, 28)Main API:
MnistGenerator(...): loads the raw model and initializes the C runtime.generate(label, seed=None): returns one28x28uint8image for label0..9.generate_many(labels, seed=None): generates a list of images.set_temperature(value): updates sampling temperature in the live generator.set_seed(value): resets the generator RNG.
Defaults:
- model:
Qwen3-2M-MNIST-GRPO.raw - shared library:
libqwen3_mnist_realtime.dylib - threads:
5 - temperature:
0.8
Small matplotlib smoke test and throughput check.
python test_realtime.pyUseful options:
python test_realtime.py --calls 10 --temperature 0.8 --seed 1234 --out realtime_10_samples.png
python test_realtime.py --model Qwen3-2M-MNIST-GRPO.raw --threads 5 --calls 100The script prints model load time, images/sec, and tokens/sec, then writes a
sample grid PNG. Labels cycle through 0..9.
Realtime Pygame viewer. It loads both the fast C-backed generator and the
default mlx_lm generation path, then displays each generated image as it
arrives. Instead of replacing one large image, the viewer adds 56 px images
row-wise to a 10x10 grid, clears the grid when the selected digit or backend
changes, and wraps back to the first slot after all 100 slots are filled.
python realtime_gui.pyControls:
- Click digit buttons
0..9to choose the target label. - Click
FASTorMLXto switch generation backend. - Press number keys
0..9to choose the target label. - Press
f,m, orTabto switch generation backend. - Press
qorEscto quit.
Useful options:
python realtime_gui.py --label 7 --temperature 0.8 --seed 1234
python realtime_gui.py --backend mlx --mlx-model Qwen3-2M-MNIST-GRPO
python realtime_gui.py --threads 5 --max-fps 120
python realtime_gui.py --headless-frames 20--headless-frames uses SDL's dummy video driver and exits after rendering the
requested number of generated frames. This is useful for quick CI-style checks
without opening a window.
realtime_10_samples.png: default output fromtest_realtime.pylibqwen3_mnist_realtime.dylib: shared library built bymake