Generative minimal tattoo patterns 🪩
Tags: art, javascript, showdev, react
Published: 2022-04-10 13:06:42

I'm a huge fan of [Okan Uckun](https://okanuckun.com/)'s work with minimal line designs, they'd make for a great tattoo one day. Here are a couple of examples:

{% instagram CTKwVOEsxoV %}

{% instagram CFFZbIWlqwc %}

Their art is subtle and beautiful (& I'm a sucker for tasteful minimal design 😌). I wanted to replicate some of the simpler designs with CSS and allow them to generate themselves with some JS, and this was the result! 

<center>[bryce.io/tatterns](https://bryce.io/tatterns) (short for tattoo patterns)</center>

{% embed https://github.com/brycedorn/tatterns no-readme %}

## Features
 - Clicking on a pattern will enlarge it & encode its randomized parameters in the URL so you can share/bookmark them. Here's [one I like](https://bryce.io/tatterns?t=eyJjb2xvciI6IiM1NTUiLCJiZ0NvbG9yIjoiI2FhYSIsImludmVyc2UiOmZhbHNlLCJkaWFtZXRlciI6MTgzLCJudW1DaXJjbGVzIjo1LCJudW1MaW5lcyI6MiwickFycnMiOltbIjAuNjEiLCIwLjkxIiwiMC4wNSIsIjAuNTEiLCIwLjY2IiwiMC4wNiIsIjAuMDMiXSxbIjAuMDMiLCIwLjIzIiwiMC44MiIsIjAuNTYiLCIwLjU5IiwiMC4wMSIsIjAuMzUiXSxbIjAuMDYiLCIwLjk0IiwiMC45NSIsIjAuNDQiLCIwLjQyIiwiMC41NCIsIjAuNzYiXSxbIjAuNTkiLCIwLjk1IiwiMC4yNiIsIjAuMjIiLCIwLjQ0IiwiMC4zNSIsIjAuNjUiXSxbIjAuMzYiLCIwLjcwIiwiMC41OCIsIjAuNjAiLCIwLjc4IiwiMC43NyIsIjAuOTQiXSxbIjAuNjciLCIwLjQ0IiwiMC42NCIsIjAuNDUiLCIwLjI5IiwiMC4wNCIsIjAuMjgiXSxbIjAuNDIiLCIwLjUyIiwiMC43NiIsIjAuNzUiLCIwLjc3IiwiMC43MSIsIjAuNzAiXV19)! 
 - Hovering over a pattern will pause the auto-update timer.
 - Pressing spacebar will regenerate all patterns at once.

## Tech notes
 - [`preact`](https://preactjs.com/)/[`picostyle`](https://github.com/morishitter/picostyle) for `20Kb` total build size
 - Fast, performant animations 🔥
 - [`wmr`](https://github.com/preactjs/wmr) for near-instant builds & native TS support

## Some things I learned along the way 👨‍🏫

 - Inverse logarithm for hover state: I wanted a way to have a consistent hover experience across different sizes without re-rendering a pattern. The solution I ended up with uses `transform: scale` with a scale percentage relative to each pattern diameter. The calculation was simple in the end though I had to plot a line by hand to get to it 😄. It ended up being `1 + Math.log(maxDiameter/diameter)`, bringing me back to my calculus/geometry days.
 - `wmr` is really nice, will be using it in the future!
 - I'll never use `query-string` (or any other library) over `URLSearchParams` again.

### Smooth animations how? 🧐

Relying primarily on `transform: translate` and `transition: transform` would overload the GPU. Instead I used `position`, which trades smoother, more expensive transition animations for much cheaper animations. After experimenting with both methods this performed better on both my M1 Macbook Pro and an older Intel Macbook Air when regenerating the full page of patterns.

The browser is recalculating layout each time a pattern's internal state is updated, but this calculation is still cheaper than hundreds (or thousands) of individual GPU threads to handle complex translations! I may do more research into this as it was interesting how relying on browser recalculation was faster than offloading to GPU, which I presumed would be faster.

---

### What's next?

Not really sure, just made this for fun. I may add more geometry for additional pattern possibilities. Maybe one day I'll use it for a gallery wall with a projector? ~~Or make some NFTs?~~ Who knows. Thanks for reading and share any patterns that you like in the comments!