GRIN is a deterministic image container format designed as an upgrade to traditional images and GIFs. Instead of storing static frames or frame sequences, GRIN treats an image as a programmable field of pixels that can be dynamically grouped, addressed, and modulated over time.
- A
.grinfile is not a loop of frames. - Nearly all animation logic lives in the file header.
- Narrow, bounded scripts operate on pixel groups and can shift colors, animate values, loop or diverge, and run once or indefinitely.
- Because logic is embedded, a
.grinfile can branch, repeat, or evolve without duplicating image data. - Rendering requires only minimal runtime code. For web playback, a small amount of JavaScript is sufficient.
- GRIN is designed to be artist-friendly, portable, and low-infrastructure, while remaining fully deterministic.
- Deterministic image container format
- 5-byte pixels: RGBA + control byte
- Fixed 128-byte header
- Bounded rules block (no unbounded metadata)
- 16 addressable groups per pixel
- Lock bit per pixel
- Opcode-driven modulation
- No frame storage required
- Header lane scripts use the
:)divider token (the legacy|divider is no longer used).
This repository includes working implementations, tools, tests, and sample files.
- Web TypeScript library with Canvas renderer
<grin-player>web component- Android Kotlin library with demo app
- CLI tools for validation, inspection, encoding, and decoding
- Tests and benchmarks for web and Android
web/- TypeScript implementation, web component, demo, testsandroid/- Kotlin implementation and demo apptools/- Node.js CLI utilitiessamples/- Reference.grinfiles (valid and invalid)benchmarks/- Performance harnesstests/- Shared fixtures and generatorscore/- Reserved for a future cross-platform reference core
node tools/bin/grin-validate.js samples/minimal.grin
node tools/bin/grin-inspect.js samples/pulse_red.grin --header --rules
node tools/bin/grin-encode.js input.png output.grin
node tools/bin/grin-decode.js samples/pulse_red.grin output.png --frame 0
node tools/bin/grin-lanes.js lanes.txt --normalizecd web
npm install
npm test
npm run buildThe demo lives in web/demo/. Build first so web/demo/demo.js can load web/dist.
A minimal embed uses the tiny loader in web/embed/grin-embed.js. It finds elements
with data-grin-src, loads grin-player, and mounts playback for you.
<div data-grin-src="https://example.com/art/minimal.grin"></div>
<script type="module" src="web/embed/grin-embed.js"></script>Optional attributes:
data-grin-autoplay- set totrueto auto-start playback.data-grin-playbackrate- numeric speed multiplier (for example0.5or2).data-grin-lib(on the script tag) - overrides thegrin-playerbundle URL.
Example using a custom grin-player bundle:
<div data-grin-src="/media/pulse_red.grin" data-grin-autoplay="true"></div>
<script
type="module"
src="web/embed/grin-embed.js"
data-grin-lib="/web/dist/grin-player.js"
></script>cd android
./gradlew :demo:assembleDebugThe demo app loads sample files from android/demo/src/main/assets/samples.
Regenerate sample files with:
node scripts/generate-samples.mjs- Format specification:
grin_technical_specification_v_2.md - Architecture:
ARCHITECTURE.md - Contributing:
CONTRIBUTING.md - Security:
SECURITY.md - Samples:
samples/README.md - Docs index:
docs/README.md
- Photoshop, Illustrator, and GIMP plugins
- Translation of
.grinfiles to real DMX stage control - Compatibility with large video walls and LED arrays
THIS REPOSITORY IS CURRENTLY UNLICENSED.
You may build, experiment, and explore this code for personal and research purposes.
You may not redistribute the code, incorporate it into unrelated projects, or use the code or its outputs for commercial purposes without explicit consent.
For commercial use, redistribution, or related project releases, contact:
This standard is intentionally constrained for security reasons and to encourage thoughtful, novel usage.