this is the renderer behind Swatches, a 7,777-piece on-chain art collection. every token points its animation_url here, so when you open a Swatches NFT in a wallet or marketplace you're not staring at a flat jpeg — you're looking at this, live and interactive.
each piece is a little grid of colored shapes you can play with: poke them, rotate the whole thing, switch render modes, flip the theme. leave it alone for a few seconds and it starts cycling colors on its own.
NFT metadata can carry both an image (static) and an animation_url (interactive/animated). marketplaces render the animation_url in an iframe when you view the token. so this repo is the art for every token — the contract just points a viewer at a URL like:
https://<host>/items/1234/
and this app looks up token #1234, builds its grid, and renders it.
flowchart LR
A[NFT contract] -->|"animation_url<br/>/items/:id/"| B[Next.js page]
B --> C[lookup token in<br/>metadata.json]
C --> D[shapeGrid + colorsGrid]
D --> E[Canvas renders<br/>3×3 grid]
E <-->|hover / click / keys| F((you))
- the whole collection lives as data in
src/config/metadata.json— one entry per token id (1–7777), each with a 3×3shapeGrid(which shapes go where) and a matchingcolorsGrid(the palette). src/pages/items/[id]/index.tsxstatically generates a page for every single token at build time.- everything after that is client-side. no server, no phoning home. it's a static site.
once a piece is on screen you can mess with it:
| control | keys | what it does |
|---|---|---|
| mode | q w e |
rounded · square · hypnotic (gradient bloom) |
| size | a s d |
tiny · medium · jumbo |
| rotate | arrow keys | spins the grid in 90° steps |
| theme | z |
light / dark |
| cursor | x |
toggle the custom cursor |
there's also a drawer menu (the bottom sheet) if you'd rather tap than type. and if you stop touching it for ~3 seconds it goes idle and shuffles the colors on its own, which is honestly the best way to watch it.
you can force the starting theme with a query param too: /items/1234/?theme=dark.
- Next.js 13 (pages router), configured for static export → deployed to Fleek / IPFS
- Framer Motion for the spring physics and transitions
- SCSS modules + Tailwind, CSS variables for theming
- Radix + Vaul for the drawer/accordion UI
- TypeScript, strict mode
pnpm install
pnpm dev # localhost:3000
pnpm build # static export → ./out/
pnpm start # preview the production build
pnpm lintsrc/
├── pages/items/[id]/ # the renderer — one static page per token
├── config/metadata.json # all 7,777 token definitions (shapes + colors)
├── components/
│ ├── Canvas/ # builds the grid, runs the idle loop, handles keys
│ ├── Shape/ # individual shape (rounded / square modes)
│ ├── GradientShape/ # the "hypnotic" gradient mode
│ ├── Drawer/ # bottom-sheet controls
│ └── StickyCursor/ # custom cursor that fades when idle
├── context/ # global state (mode, size, rotation, theme)
├── helpers/ # color math + grid shuffling
└── styles/ # globals, theme vars, mixins
metadata.jsonis big (it's the entire collection), so the first build takes a minute. that's expected.- it's a fully static export on purpose — so it can live on IPFS and keep working long after I stop paying for anything.