TarulAhsan/ShrinkRay

HandBrake for TinyML - quantize and compress small neural networks (Keras, TFLite, ONNX, scikit-learn) for ESP32, STM32, nRF52, RP2040 and ATmega microcontrollers, then get a FITS / TIGHT / TOO BIG verdict with a proof report before you flash. 100% local CLI - no cloud, no telemetry.

2

stars

2

commits

Python

primary language

Aug 25, 2026

updated

TarulAhsan.github.io/shrinkray/
arduino
cli-tool
edge-ai
embedded-systems
esp32
iot
keras
machine-learning
microcontroller
model-compression
nrf52840
onnx
python
quantization
rp2040
scikit-learn
stm32
tensorflow-lite
tflite-micro
tinyml

README

ShrinkRay

CI License: AGPL v3 Python 3.10–3.12 Platforms

HandBrake for TinyML — one tool that takes any small model and produces a chip-ready artifact, with a report proving it fits.

📖 New here? Read the full user guide — installation, every command, and troubleshooting.

ShrinkRay compresses/quantizes small neural networks for microcontrollers and tells you — before you touch a firmware build — whether the result fits your target chip's flash and RAM.

  • FITS / ⚠️ TIGHT / ❌ TOO BIG verdict per chip
  • Keras/TFLite → int8/int16/float TFLite + C byte array (.cc/.h) for LiteRT-M / CMSIS-NN
  • scikit-learn → single portable C99 header (via emlearn; works even on ATmega328P)
  • ONNX → .espdl via ESP-PPQ (optional extra)
  • Self-contained report.md + report.html after every conversion
  • 100% local: no network calls, no telemetry, no account

Demo

demo

30-second quickstart

pip install shrinkray-cli

shrinkray chips                                # the 12-chip database
shrinkray inspect model.keras                  # params, size, ops, FLOPs
shrinkray convert model.keras --target esp32s3 --method int8 --data cal.npy

Output:

wrote outputs/model_int8.tflite (2.7 KiB)
wrote outputs/model_int8.cc
wrote outputs/model_int8.h
accuracy max-abs-error 0.002717, top-1 agreement 100.0%
esp32s3: ✅ FITS — flash 2.7 KiB / 8192.0 KiB (0.0%), est. RAM 0.1 KiB / 512.0 KiB (0.0%)
wrote outputs/report.md
wrote outputs/report.html

--data cal.npy is a small float32 array of representative samples (numpy.save("cal.npy", x[:100].astype("float32"))). With it you get full-integer quantization and an accuracy-delta check; without it, int8 falls back to dynamic-range quantization.

Commands

CommandWhat it does
shrinkray inspect <model>Format, parameter count, file size, operator histogram, FLOP estimate for .keras / .h5 / .tflite / .onnx / .pkl
shrinkray chips [--chips extra.json]Print the chip database; merge your own chips from JSON
shrinkray convert <model> --target <chip>Convert + fit verdict + reports

Useful convert flags:

FlagMeaning
--method int8|int16|float|inlineint16 needs --data; inline is for scikit-learn .pkl
--data cal.npyCalibration samples (full-integer quant + accuracy check)
--out DIROutput directory (default: outputs/)
--all-chipsFit-check against the whole database, ranked by headroom (always exits 0)
--chips mychips.jsonMerge custom chip definitions

Exit codes: 0 ok · 1 model error · 2 missing optional dependency · 3 converted but doesn't fit the target.

Config file

Drop a shrinkray.toml in your project directory to stop repeating flags (CLI flags always win over the file):

[defaults]
target = "esp32s3"
data = "cal.npy"
out = "build"

Chip database

Ships with 12 chips: esp32, esp32s3, esp32p4, stm32f407, stm32h743, stm32u575, nrf52840, nrf5340, rp2040, rp2350, nano33ble, atmega328p. Each entry: flash KiB, SRAM KiB, clock MHz, feature flags (dsp / simd / ai-instructions), preferred runtime (litert-m / cmsis-nn / esp-dl / emlearn).

Extend it with your own JSON (--chips mychips.json; your entries override built-ins on name collision):

{"chips": [{"name": "myboard", "flash_kb": 512, "sram_kb": 128,
            "clock_mhz": 100, "features": ["dsp"], "runtime": "cmsis-nn"}]}

Fit model

  • Flash = actual artifact bytes measured on disk.
  • Peak RAM = tensor-liveness analysis of the TFLite graph: the largest sum of concurrently live activation tensors × 1.2 safety factor.
  • Verdicts: FITS = flash ≤ 90% and RAM ≤ 70%; TIGHT = flash ≤ 100% and RAM ≤ 90%; otherwise TOO BIG.

Install matrix

On PyPI the package is shrinkray-cli (the name shrinkray was already taken by an unrelated project). The command it installs is still shrinkray.

MethodCommandNotes
pip (Win/Linux/macOS)pip install shrinkray-cliPython 3.10–3.12, CPU-only is fine
pip + ESP-DL pipelinepip install shrinkray-cli[espdl]adds ESP-PPQ (heavier deps)
pipxpipx install shrinkray-cliisolated CLI install
Standalone binaryGitHub Releasesexperimental; bundles TF, so it's big — pip is recommended
winget / brewplannednot published yet

Honest limitations (v0.1)

  • No GUI, no training — input is an already-trained model ≤ ~100 MB.
  • Pipeline A (Keras→TFLite) requires TensorFlow (CPU build is fine). Everything else — TFLite inspect, sklearn pipeline, chips, reports — works without it.
  • RAM estimates are heuristics, not measurements: TFLite liveness ×1.2 for Pipeline A; not estimable for emlearn C code (reported as n/a).
  • Quantizing an already-.tflite file is not supported — pass the original .keras/.h5 (or use --method float to repackage as a C array).
  • Pipeline C requires pip install shrinkray-cli[espdl]; without it the command prints install instructions and exits 2 — everything else keeps working.
  • No ONNX Runtime quantization, no RISC-V paths, no model zoo (v0.2+).

Development

pip install -e ".[dev]"
pytest --cov=shrinkray.core --cov=shrinkray.pipelines   # coverage gate: 80%

CI runs pytest on Windows / Ubuntu / macOS × Python 3.10 / 3.11 / 3.12 (.github/workflows/test.yml); tag pushes build standalone binaries (.github/workflows/release.yml).

Test fixtures (tiny Keras net, sklearn forest, ONNX graph) are generated at runtime — no binary models in the repo.

License

GNU Affero General Public License v3.0 (AGPL-3.0-only) — see LICENSE.

Free for everyone to use, study, and modify — including commercial internal use. If you distribute ShrinkRay or a modified version, or offer it as a network service, you must share your source under the same license. Commercial closed-source licensing is available on request; contact the maintainer.

Contributors

tarulahsan

2 commits

TarulAhsan/ShrinkRay

HandBrake for TinyML - quantize and compress small neural networks (Keras, TFLite, ONNX, scikit-learn) for ESP32, STM32, nRF52, RP2040 and ATmega microcontrollers, then get a FITS / TIGHT / TOO BIG verdict with a proof report before you flash. 100% local CLI - no cloud, no telemetry.

2

stars

2

commits

Python

primary language

Aug 25, 2026

updated

TarulAhsan.github.io/shrinkray/
arduino
cli-tool
edge-ai
embedded-systems
esp32
iot
keras
machine-learning
microcontroller
model-compression
nrf52840
onnx
python
quantization
rp2040
scikit-learn
stm32
tensorflow-lite
tflite-micro
tinyml

README

ShrinkRay

CI License: AGPL v3 Python 3.10–3.12 Platforms

HandBrake for TinyML — one tool that takes any small model and produces a chip-ready artifact, with a report proving it fits.

📖 New here? Read the full user guide — installation, every command, and troubleshooting.

ShrinkRay compresses/quantizes small neural networks for microcontrollers and tells you — before you touch a firmware build — whether the result fits your target chip's flash and RAM.

  • FITS / ⚠️ TIGHT / ❌ TOO BIG verdict per chip
  • Keras/TFLite → int8/int16/float TFLite + C byte array (.cc/.h) for LiteRT-M / CMSIS-NN
  • scikit-learn → single portable C99 header (via emlearn; works even on ATmega328P)
  • ONNX → .espdl via ESP-PPQ (optional extra)
  • Self-contained report.md + report.html after every conversion
  • 100% local: no network calls, no telemetry, no account

Demo

demo

30-second quickstart

pip install shrinkray-cli

shrinkray chips                                # the 12-chip database
shrinkray inspect model.keras                  # params, size, ops, FLOPs
shrinkray convert model.keras --target esp32s3 --method int8 --data cal.npy

Output:

wrote outputs/model_int8.tflite (2.7 KiB)
wrote outputs/model_int8.cc
wrote outputs/model_int8.h
accuracy max-abs-error 0.002717, top-1 agreement 100.0%
esp32s3: ✅ FITS — flash 2.7 KiB / 8192.0 KiB (0.0%), est. RAM 0.1 KiB / 512.0 KiB (0.0%)
wrote outputs/report.md
wrote outputs/report.html

--data cal.npy is a small float32 array of representative samples (numpy.save("cal.npy", x[:100].astype("float32"))). With it you get full-integer quantization and an accuracy-delta check; without it, int8 falls back to dynamic-range quantization.

Commands

CommandWhat it does
shrinkray inspect <model>Format, parameter count, file size, operator histogram, FLOP estimate for .keras / .h5 / .tflite / .onnx / .pkl
shrinkray chips [--chips extra.json]Print the chip database; merge your own chips from JSON
shrinkray convert <model> --target <chip>Convert + fit verdict + reports

Useful convert flags:

FlagMeaning
--method int8|int16|float|inlineint16 needs --data; inline is for scikit-learn .pkl
--data cal.npyCalibration samples (full-integer quant + accuracy check)
--out DIROutput directory (default: outputs/)
--all-chipsFit-check against the whole database, ranked by headroom (always exits 0)
--chips mychips.jsonMerge custom chip definitions

Exit codes: 0 ok · 1 model error · 2 missing optional dependency · 3 converted but doesn't fit the target.

Config file

Drop a shrinkray.toml in your project directory to stop repeating flags (CLI flags always win over the file):

[defaults]
target = "esp32s3"
data = "cal.npy"
out = "build"

Chip database

Ships with 12 chips: esp32, esp32s3, esp32p4, stm32f407, stm32h743, stm32u575, nrf52840, nrf5340, rp2040, rp2350, nano33ble, atmega328p. Each entry: flash KiB, SRAM KiB, clock MHz, feature flags (dsp / simd / ai-instructions), preferred runtime (litert-m / cmsis-nn / esp-dl / emlearn).

Extend it with your own JSON (--chips mychips.json; your entries override built-ins on name collision):

{"chips": [{"name": "myboard", "flash_kb": 512, "sram_kb": 128,
            "clock_mhz": 100, "features": ["dsp"], "runtime": "cmsis-nn"}]}

Fit model

  • Flash = actual artifact bytes measured on disk.
  • Peak RAM = tensor-liveness analysis of the TFLite graph: the largest sum of concurrently live activation tensors × 1.2 safety factor.
  • Verdicts: FITS = flash ≤ 90% and RAM ≤ 70%; TIGHT = flash ≤ 100% and RAM ≤ 90%; otherwise TOO BIG.

Install matrix

On PyPI the package is shrinkray-cli (the name shrinkray was already taken by an unrelated project). The command it installs is still shrinkray.

MethodCommandNotes
pip (Win/Linux/macOS)pip install shrinkray-cliPython 3.10–3.12, CPU-only is fine
pip + ESP-DL pipelinepip install shrinkray-cli[espdl]adds ESP-PPQ (heavier deps)
pipxpipx install shrinkray-cliisolated CLI install
Standalone binaryGitHub Releasesexperimental; bundles TF, so it's big — pip is recommended
winget / brewplannednot published yet

Honest limitations (v0.1)

  • No GUI, no training — input is an already-trained model ≤ ~100 MB.
  • Pipeline A (Keras→TFLite) requires TensorFlow (CPU build is fine). Everything else — TFLite inspect, sklearn pipeline, chips, reports — works without it.
  • RAM estimates are heuristics, not measurements: TFLite liveness ×1.2 for Pipeline A; not estimable for emlearn C code (reported as n/a).
  • Quantizing an already-.tflite file is not supported — pass the original .keras/.h5 (or use --method float to repackage as a C array).
  • Pipeline C requires pip install shrinkray-cli[espdl]; without it the command prints install instructions and exits 2 — everything else keeps working.
  • No ONNX Runtime quantization, no RISC-V paths, no model zoo (v0.2+).

Development

pip install -e ".[dev]"
pytest --cov=shrinkray.core --cov=shrinkray.pipelines   # coverage gate: 80%

CI runs pytest on Windows / Ubuntu / macOS × Python 3.10 / 3.11 / 3.12 (.github/workflows/test.yml); tag pushes build standalone binaries (.github/workflows/release.yml).

Test fixtures (tiny Keras net, sklearn forest, ONNX graph) are generated at runtime — no binary models in the repo.

License

GNU Affero General Public License v3.0 (AGPL-3.0-only) — see LICENSE.

Free for everyone to use, study, and modify — including commercial internal use. If you distribute ShrinkRay or a modified version, or offer it as a network service, you must share your source under the same license. Commercial closed-source licensing is available on request; contact the maintainer.

Contributors

tarulahsan

2 commits

Languages

Python

100.0%