Storing Sprites in Source — Native Rendering on Gno.land
Gno.land doesn’t give you a DOM. It doesn’t give you HTML or JavaScript or CSS.
But it does give you access to your own realm's source, and if you squint at it sideways, it gives you something better: permissioned, procedural content registration, and just enough I/O to express entire dapps using code alone.
I’ve been experimenting with these boundaries in a module I’m calling sprite
.
📦 Code-as-Sprite: Procedural Pixel Images in Gno
In sprite
, you don’t store image data — you define a shape.
Each sprite:
- declares its own
Bounds()
, and - paints pixels by passing
p(x, y, r, g, b)
into a callback.
Like this:
goCopyEditfunc (Gnome) Pixels(p PixelSetter) {
p(9, 22, 254, 254, 254)
p(9, 23, 250, 251, 251)
p(9, 24, 239, 244, 243)
}
These sprites are registered by name:
goCopyEditsprite.Register("gnome", Gnome{})
🔐 Permission Model
Gno.land gives you fine-grained control over who can mutate what using realm paths and source introspection.
So in sprite
, registration is gated by two things:
- Caller's realm must be a sub-path of the module:
goCopyEdit/r/stackdump/sprite/v0.0.1/gnome // ✅ allowed
/r/notstackdump/rogue // ❌ denied
- The sprite name must match the final part of the path:
goCopyEdit/r/stackdump/sprite/v0.0.1/gnome → name: "gnome" // ✅ valid
This gives us a self-sovereign content registry that lives entirely on-chain and in-code.
🎨 The Widget: A Dapp Without a Server
What if you want to author these pixel blobs visually? I built a tiny HTML+JS pixelizer that runs in your browser:
goCopyEditfunc (Dapp) DataUrl() string {
return "data:text/html;utf8," + url.PathEscape(htmlContent)
}
This data:
URL is safe to embed in Gno.markdown, even on-chain, even without a frontend.
The widget:
- lets you upload an image
- choose grid size
- preview an SVG
- export Gno code like:
p(10, 5, 255, 255, 255)
You can paste the output directly into your realm. Your sprite becomes code, not asset. Immutable, composable, auditable.
🧬 Storing Code, Not Data
This has become a theme for me: Why store binary blobs when you can emit source?
Sprites are source. Workflows are source. Views, even dapps, can be stored as source.
In sprite
, everything lives in your realm:
- your pixel logic,
- your asset identity,
- your visual index.
And you can inspect it all, because it's just code.
🪞 Try It Out
You can explore the current sprite registry here: 👉 https://github.com/stackdump/gno/tree/sprite_v1
Click around, view sources, generate your own.
Want to submit a sprite? Fork the realm, use the widget, and post the result back.
🧠 Final Thoughts
Gno.land isn’t limited, it’s minimal — and minimalism forces creativity.
I used to think you couldn’t render anything expressive without hosting a site. But it turns out: if you’re willing to write the renderer, you don’t need a site at all.
Just code. Just intent. Just sprites.