217 lines
8.3 KiB
Markdown
217 lines
8.3 KiB
Markdown
|
---
|
|||
|
title: rainbow quox
|
|||
|
date: 2024-12-03
|
|||
|
tags: [computer, website, fursona, december adventure]
|
|||
|
summary: q.t. colour scheme generator
|
|||
|
header-includes: |
|
|||
|
<style>
|
|||
|
#relcolor {
|
|||
|
max-width: 20em; margin: auto;
|
|||
|
display: grid; grid-template-columns: 1fr 1fr; gap: .5em;
|
|||
|
font-weight: bold;
|
|||
|
--hi: #ea9aa1; --wow: oklch(from var(--hi) l c calc(h + 180));
|
|||
|
}
|
|||
|
#relcolor div {
|
|||
|
text-align: center; font-size: 125%;
|
|||
|
padding: .4em; border-radius: .5em;
|
|||
|
color: black;
|
|||
|
background: var(--bg);
|
|||
|
border: 4px solid oklch(from var(--bg) .25 75% h);
|
|||
|
}
|
|||
|
#hi { --bg: var(--hi); }
|
|||
|
#wow { --bg: var(--wow); }
|
|||
|
|
|||
|
#patterns { display: flex; gap: 1em; }
|
|||
|
#patterns figure { margin: 0; }
|
|||
|
|
|||
|
#result {
|
|||
|
display: grid;
|
|||
|
grid-template-columns: 1fr 1fr;
|
|||
|
align-items: center;
|
|||
|
}
|
|||
|
|
|||
|
svg { height: 1.25em; vertical-align: -20%; }
|
|||
|
rect { stroke: currentcolor; stroke-width: 0.075; }
|
|||
|
</style>
|
|||
|
...
|
|||
|
|
|||
|
the animal-inclined might know that [q.t.][qt] can change its colour any time it wants. if you click that link you can clearly see i have some tendencies, but it can in theory be anything. so something i have wanted for a while is a page where you can click a button and get a bespoke randomly-generated quox theme of your very own.
|
|||
|
|
|||
|
so for today's [`@december adventure@`][decadv], i did that. (yesterday's was retroactively the AOC stuff.)
|
|||
|
|
|||
|
[decadv]: https://eli.li/december-adventure
|
|||
|
|
|||
|
:::banner
|
|||
|
[go here if you just want to play with the thing][thing]
|
|||
|
:::
|
|||
|
|
|||
|
[thing]: https://yummy.cricket/rainbow-quox
|
|||
|
|
|||
|
you can also skip to [what i _actually_ ended up doing](#actual), if you don't care about the false starts.
|
|||
|
|
|||
|
[qt]: https://gallery.niss.website/by-any/#qt
|
|||
|
|
|||
|
# doing that
|
|||
|
|
|||
|
{.floating .expandable .nobg .shaped}
|
|||
|
|
|||
|
pretty much what i want to do, at least to begin with, is take the original colours of the image and move the hues around at random.
|
|||
|
|
|||
|
if you look at [mdn], you might see this interesting [`hue-rotate()`][hr] thing that might do what i want. let's have a look.
|
|||
|
|
|||
|
[mdn]: https://developer.mozilla.org/en-US/docs/Web/CSS
|
|||
|
[hr]: https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/hue-rotate
|
|||
|
|
|||
|
> **Note:** `hue-rotate()` is specified as a matrix operation on the RGB color. It does not actually convert the color to the HSL model, which is a non-linear operation. Therefore, it may not preserve the saturation or lightness of the original color, especially for saturated colors.
|
|||
|
|
|||
|
|
|||
|
{.floating .right .nobg .expandable .shaped}
|
|||
|
|
|||
|
well that doesn't sound very encouraging. but maybe it'll be fine.
|
|||
|
|
|||
|
i shoved [all of the colours][jofo] to hue 0° (using krita's _hue HSL_ blend mode), and used `hue-rotate()` to change it back to the 'main' colour of each region.
|
|||
|
|
|||
|
it won't look exact, but it'll be close. right?
|
|||
|
|
|||
|
[jofo]: https://johnnyforeigner.bandcamp.com/track/all-of-the-colours
|
|||
|
|
|||
|
{.expandable .nobg}
|
|||
|
|
|||
|
well that's no good at all. i guess that warning was serious.
|
|||
|
|
|||
|
# ok what about blend modes
|
|||
|
|
|||
|
all right, fine. what else. as a chronic over-user of `overlay`, i can certainly tell you that css has a few [blending modes][bm]. not as many as krita, which has approximately "too many", but enough for most purposes. one of them is `hue`. how about that.
|
|||
|
|
|||
|
[bm]: https://developer.mozilla.org/en-US/docs/Web/CSS/blend-mode
|
|||
|
|
|||
|
this takes a bit more messing, because i need to create a flood fill of one of the colours from that layer, and blend with that. so how about an SVG filter, i guess. or, six SVG filters---one for each layer, since you can't parametrise them. each one looks like this, with a different `flood-color`.
|
|||
|
|
|||
|
```svg
|
|||
|
<filter id="f-outer">
|
|||
|
<feFlood result="hue" flood-color="#57267e" />
|
|||
|
<feBlend in="hue" in2="SourceGraphic" mode="hue" result="res" />
|
|||
|
<feComposite in="res" in2="SourceGraphic" operator="in" />
|
|||
|
</filter>
|
|||
|
```
|
|||
|
|
|||
|
and…
|
|||
|
|
|||
|
{.expandable .nobg}
|
|||
|
|
|||
|
i was expecting at least the same thing, but a different, also wrong, result is pretty cool.
|
|||
|
|
|||
|
# drastic measures {#actual}
|
|||
|
|
|||
|
ok, enough messing around, time to bite the bullet. separate _every_ colour into its own layer, and use those as masks for colour fills.
|
|||
|
|
|||
|
```css
|
|||
|
#spines {
|
|||
|
background: oklch(30.77% 0.1306 var(--hue)); --hue: 298.19;
|
|||
|
mask-image: url(front/spines.png);
|
|||
|
}
|
|||
|
/* etc… */
|
|||
|
```
|
|||
|
|
|||
|
since the hue is separated out into a variable, i can just randomise them all and instantly have _something_ working.
|
|||
|
|
|||
|
{.expandable .hasborder #chaos}
|
|||
|
|
|||
|
|
|||
|
# digression about relative colours
|
|||
|
|
|||
|
[» skip »](#js)
|
|||
|
|
|||
|
another thing i played with was using [`@relative colours@`][relcol] to generate a whole palette from one starting colour. relative colours work like this: if you have an existing colour `--hi`, you can rotate its hue by half a turn (in real `oklch` this time!!!), keeping its luma and chroma the same, by saying this:
|
|||
|
|
|||
|
```css
|
|||
|
:root {
|
|||
|
--hi: #ea9aa1;
|
|||
|
--wow: oklch(from var(--hi) l c calc(h + 180));
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
<div id=relcolor>
|
|||
|
<div id=hi>\--hi</div>
|
|||
|
<div id=wow>\--wow</div>
|
|||
|
</div>
|
|||
|
|
|||
|
[relcol]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_colors/Relative_colors
|
|||
|
|
|||
|
so i tried that. after guessing a bunch of relative colours, i ended up with this:
|
|||
|
|
|||
|
{.expandable .nobg}
|
|||
|
|
|||
|
|
|||
|
# just do it in ~~java~~typescript. fine {#js}
|
|||
|
|
|||
|
that was pretty cool, but i decided i actually want more flexibility. lighter and darker colour schemes. triadic schemes. whatever.
|
|||
|
|
|||
|
maybe that would be possible in pure css with increasingly illegible relative colours. but the randomise button is going to need javascript anyway, so i might as well just fill in the colours directly. but `oklch` is still useful for generating consistent palettes.
|
|||
|
|
|||
|
the constraints for a "good" q.t. are something like:
|
|||
|
|
|||
|
1. the spines should be a similar colour to its main body.
|
|||
|
2. the same applies to the four fin colours; the belly colours; the sock colours; and the mask and claw colours.
|
|||
|
3. the belly should be very different from the rest of the body.
|
|||
|
4. the eyes should be a similar colour to the body, the fins, or the belly.
|
|||
|
5. the vitiligo patches should match the colour that they are on.
|
|||
|
6. the sock stripes should be pale and not too saturated.
|
|||
|
|
|||
|
a good place to start is the same place everyone does: with complementary and triadic colours.
|
|||
|
|
|||
|
:::{#patterns}
|
|||
|
{.expandable .nobg}
|
|||
|
|
|||
|
{.expandable .nobg}
|
|||
|
|
|||
|
{.expandable .nobg}
|
|||
|
:::
|
|||
|
|
|||
|
using this as a starting point, i pick some evenly-spaced colours for each bit. the final palettes come out like this!
|
|||
|
|
|||
|
:::{#result}
|
|||
|
- fins:
|
|||
|
<svg viewBox='-0.1 -0.1 8.7 1.2'>
|
|||
|
<rect fill=#770084 width=2 height=1 />
|
|||
|
<rect fill=#9d0058 width=2 height=1 x=2 />
|
|||
|
<rect fill=#a11916 width=2 height=1 x=4 />
|
|||
|
<rect fill=#eead91 width=2 height=1 x=6.5 />
|
|||
|
</svg>
|
|||
|
- body:
|
|||
|
<svg viewBox='-0.1 -0.1 6.7 1.2'>
|
|||
|
<rect fill=#00709b width=2 height=1 />
|
|||
|
<rect fill=#008fca width=2 height=1 x=2 />
|
|||
|
<rect fill=#7dd1f1 width=2 height=1 x=4.5 />
|
|||
|
</svg>
|
|||
|
- belly:
|
|||
|
<svg viewBox='-0.1 -0.1 8.7 1.2'>
|
|||
|
<rect fill=#dc8d7b width=2 height=1 />
|
|||
|
<rect fill=#efa4b0 width=2 height=1 x=2 />
|
|||
|
<rect fill=#f9aba1 width=2 height=1 x=4.5 />
|
|||
|
<rect fill=#e3adbc width=2 height=1 x=6.5 />
|
|||
|
</svg>
|
|||
|
- mask/claws/socks:
|
|||
|
<svg viewBox='-0.1 -0.1 9.2 1.2'>
|
|||
|
<rect fill=#ebc1c8 width=2 height=1 />
|
|||
|
<rect fill=#b4aaae width=2 height=1 x=2.5 />
|
|||
|
<rect fill=#ffc1a2 width=2 height=1 x=5 />
|
|||
|
<rect fill=#c46c49 width=2 height=1 x=7 />
|
|||
|
</svg>
|
|||
|
|
|||
|
{ .right .expandable .nobg}
|
|||
|
:::
|
|||
|
|
|||
|
# ok what now
|
|||
|
|
|||
|
it's not finished. some things i plan to add at, uh, some point, include:
|
|||
|
|
|||
|
- put [chaos mode](#chaos) back in.
|
|||
|
- permalinks for colour schemes. to do this i need to write another random number generator, since `Math.random` can't be manually seeded. and by "write" i mean "look up online".
|
|||
|
- colour schemes with a light body and dark belly, rather than the other way around.
|
|||
|
- render onto a `<canvas>`, or something. whatever's needed to be able to save the images without having to take a screenshot.
|
|||
|
|
|||
|
:::banner
|
|||
|
[here's the link again to save you scrolling back up][thing]
|
|||
|
:::
|