This commit is contained in:
rhiannon morris 2024-12-03 17:54:47 +01:00
parent 4f30b1e3c6
commit 0385c3215b
26 changed files with 535 additions and 274 deletions

View file

@ -1,274 +0,0 @@
---
title: rainbow quox
date: 2024-11-17
tags: [computer, website, fursona]
summary: q.t. colour scheme generator
...
so how about that regular posting, huh. ha ha ha
i haven't been up to much unusual. [drawing], mostly. i installed [nixos] on both my computers and it's going pretty well so far [(less than a week)]{.note}, but every computer toucher does that at some point, so, whatever.
[drawing]: https://gallery.niss.website
[nixos]: https://nixos.org
anyway, to the point.
:::banner
[go here if you just want to play with the thing][thing]
<!-- TODO add this link!!! -->
:::
# the point {.unnumbered}
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 i did that.
:::aside
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
![this is the image i'm starting from.](rainbow-quox/front.avif){.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.
well that doesn't sound very promising. but maybe it'll be fine, so let's try it. first, separate the pictures from [q.t.'s refsheet][ref] into bits. like this.
[ref]: https://gallery.niss.website/main/niss/2024-06-27-qt
![sock stripes, belly, main body, and masks/claws should change independently from each other. the other colours don't change.](rainbow-quox/pieces1.avif){.expandable .lightbg}
now let's layer them back on top of each other with some css.
```html
<!doctype html>
<style>
#container {
width: 100%; position: relative;
img { position: absolute; inset: 0; }
}
</style>
<div id=container>
<img src=outer.png> <img src=belly.png> <img src=eyes.png>
<img src=tongues.png> <img src=collars.png> <img src=masks.png>
<img src=socks.png> <img src=stripes.png> <img src=lines.png>
</div>
```
![took a hot shower for too long](rainbow-quox/red.avif){.floating .left .nobg .expandable .shaped}
ok, next, actually try to do the hue stuff. to check it works at all, i shoved everything 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.
```css
#container img { filter: hue-rotate(var(--hue)); }
#outer { --hue: 273deg; } #belly { --hue: 26deg; }
#eyes { --hue: 133deg; } #masks { --hue: 284deg; }
#stripes { --hue: 188deg; } #lines { --hue: 273deg; }
/* also add the id to each image */
```
right?
![oh](rainbow-quox/dark.avif){.expandable .nobg}
well that's no good at all. i guess that warning was serious.
# ok what about blend modes
![shoutout to the four different types of "soft light"](rainbow-quox/blends.avif){.floating .expandable}
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 filter, since you can't parametrise them.
time to copy and paste the same five lines six times. yaaaaaaaaay
![ugh](../images/bub_flop.png){.bigemoji .pixel} \
```svg
<svg style="position: absolute">
<filter id="fouter">
<feFlood result="hue" flood-color="#57267e" />
<feBlend in="hue" in2="SourceGraphic" mode="hue" result="res" />
<!-- ↓ without this the background will also be filled in -->
<feComposite in="res" in2="SourceGraphic" operator="in" />
</filter>
<!-- …and same for the others, with different flood-colors -->
</svg>
```
```css
#outer { filter: url(#fouter); }
/* …etc… */
```
and…
![also no](rainbow-quox/dark2.avif){.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 single colour into its own layer, and use those as _masks_ for colour fills.
now the pieces look like _this_:
![this image is probably incomprehensible now. it doesn't really matter---the main thing is that there are seventeen different layers.](rainbow-quox/pieces2.avif){.expandable .lightbg}
the colours in the images no longer matters, only the alpha channel. [(except for the eyes.)]{.note} each one is just a mask over a background fill of the right colour.
```css
@layer {
#container { position: relative; width: 90vw; aspect-ratio: 3439/2240; }
#container div { position: absolute; inset: 0; mask-size: contain; }
}
@layer {
#static { background: url(front/static.png) 0 0 / contain; }
#eye-shine {
background: url(front/eyes.png) 0 0 / contain;
mix-blend-mode: luminosity;
}
/* the others all look like this: */
#spines {
background: oklch(30.77% 0.1306 var(--hue)); --hue: 298.19;
mask-image: url(front/spines.png);
}
/* etc… */
}
```
```html
<div id=container>
<!-- divs not images now. the images are all in the background properties -->
<div class=part id=static></div>
<div class=part id=spines></div>
<div class=part id=stripes></div>
<!-- etc… -->
<div id=eye-shine></div>
</div>
```
since the hue is separated out into a variable, i can just do:
```js
for (const elem of document.getElementsByClassName('part')) {
elem.style.setProperty('--hue', Math.random() * 360);
}
```
and instantly i have _something_ working. i used `oklch` because it was more likely than `hsl` or whatever to keep the colours the same kind of distance from each other, since that is what it's designed for.
![some of these are kinda promising already??](rainbow-quox/quoxes1.avif){.expandable .hasborder}
# keeping the colours in sync
so as of last year, most browsers got a thing called [relative colours]. if you have an existing colour `--hi`, you can rotate its hue by half a turn by saying something like
[relative colours]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_colors/Relative_colors
```css
:root {
--hi: #ea9aa1;
--wow: oklch(from var(--hi) l c calc(h + 180));
}
```
---
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); }
</style>
...
<div id=relcolor>
<div id=hi>\--hi</div>
<div id=wow>\--wow</div>
</div>
you're taking the value of `var(--hi)`, keeping the lightness and chroma channels the same, and adding 180° to the hue.
:::aside
that's not quite true. safari, as always, does it slightly wrong. according to the spec, all channels in a relative colour must be dimensionless, but in safari, the hue is an `<angle>`. other browsers, following the spec correctly, _don't allow_ that. so you _actually_ have to write
```css
:root {
--hi: #ea9aa1;
--wow: oklch(from var(--hi) l c calc(h + 180));
}
@supports (color: oklch(from red l c 10deg)) {
:root { --wow: oklch(from var(--hi) l c calc(h + 180deg)); }
}
```
thanks apple!
:::
so based on that, i can pick one initial colour and base all the others on it. like
```css
:root {
/* these aren't attempting to be the same colours, just guessing something
that MIGHT look nice */
--outer: #57267e;
--spines: oklch(from var(--outer) calc(l * .75) calc(c * 1.25) h);
--vitiligo1: oklch(from var(--outer) calc(1 - (1 - l)/4) calc(c / 2) h);
/* static l/c values because the socks are always some pale colour */
--stripes: oklch(from var(--outer) .9 12.5% calc(h + 120));
--cuffs: oklch(from var(--stripes) .8 25% h);
/* etc */
}
.outer { background: var(--outer); mask-image: url(front/outer.png); }
/* etc */
```
so after guessing a bunch of relative colours, i ended up with this:
![yeah that looks ok for a first try.](rainbow-quox/relative.avif){.expandable .nobg}
## notes for niss. if this goes online i fucked up
- palette types
- randomise distance between analogous colours
- light, dark, light-dark, dark-light (belly vs outer)
- reinstate chaos mode
- can you set the random seed in the browser
- no. anyway the algorithm might be different
- make yr own
- or <https://stackoverflow.com/a/47593315>. whatever
- if so: use that to make palette permalinks

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 536 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 652 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB