Magical gene / swaps the colour LUT
LUT
A horse that resolves its colours against an unnatural gradient.
Every melanin gene does exactly what it always does — but instead of the warm
red/orange/brown lookup, a horse homozygous for a variant allele reads its pigment
off a different palette entirely. There are two: Blupnk is blue and
pink, Grnpnk green through pink. Dreamier coat colours from the same
genotype — and a horse needs two copies of the same one, so a
Blupnk beside a Grnpnk is an ordinary horse.
Crossing two of them
Two of the same, or nothing
A LUT allele only does anything when the horse carries two identical copies of it. One copy is a carrier and shows nothing. When there is more than one variant, two different variant alleles will also be silent — a mixed pair is not “half of each palette”, it is the natural gradient. This is what makes an unnatural coat a breeding project rather than a lucky catch, and it is the same shape the milk, healer and magic body-stat loci use.
Its gene carrot
- Gene key
- horsegenetics.lut
- Priority
- 190 (magical band, after verdant)
- Alleles
- n Blupnk Grnpnk
- Combinations
- 6, all carryable — 5 outcomes (a carrier wild type and a swap per palette, plus the plain wild type; a mixed pair lands on the wild type)
- Outcomes
- wild,
bluepink-carrierandgreenpink-carrier(both wild types),bluepink,greenpink - Default allele
- n
- Wild frequency
- ~1 in 60 wild horses carries each variant copy; a homozygote is never a founder
- Founder draws
- 1 × nextFloat()
- Coat effect
- replaces the phase-2 gradient for a variant homozygote — deterministic, so it is out of the per-horse texture fingerprint
- Gene carrot
- none
The blue/pink palette has been seen in game and works. The green/pink
one has not. The golden test pins both, and a unit test walks
VARIANTS rather than naming a palette, so the claims that hold for
one hold for the other — but nothing has looked at what green-through-pink
does to a bay, and the layout of the chart is the whole of what decides that.
Checklist on To be verified.
The combination table
| Combination | Outcome | Effect |
|---|---|---|
n/n | wild | The natural red/black gradient |
Blupnk/n | bluepink-carrier | Nothing — a silent carrier, passes the allele on |
Blupnk/Blupnk | bluepink | Every melanin gene resolves against lutbluepink.png |
Grnpnk/n | greenpink-carrier | Nothing — a silent carrier, passes the allele on |
Grnpnk/Grnpnk | greenpink | Every melanin gene resolves against lutgreenpink.png |
Blupnk/Grnpnk | wild | Nothing. Two different variants are not half of each palette — they are the natural gradient |
This is the only locus that can change the LUT
There is no per-gene “use this gradient” hook and there is not going to be
one. A horse has two copies of this chromosome and no more, so it can shift its
palette at most one way at a time. Anything that should affect which
LUT a coat resolves against is added as another allele on this gene,
never as a new gene — the same rule KIT follows for
white spotting. In code this is the LutContribution interface, and
exactly one gene implements it, permanently.
Where it sits in the pipeline
LUT is magical but it does not paint. It leaves phase 1 (the melanin
restriction) completely alone and it returns no phase-3 tint. What it does is picked
up out of band by the composer before phase 2: if a
LutContribution gene reports a homozygous variant, the composer resolves
(red, black) pairs through that variant's GradientLut
instead of the base one. So a bluepink black horse is still “maximal red +
maximal black” going into the lookup; it just lands somewhere cool and dark on
a different chart. See the pipeline.
The game module loads redblackgradient.png plus every texture named by a
LutContribution gene's lutResources() into a
LutSet (base + keyed alternates) and hands that to the composer; a
missing alternate texture falls back to the base gradient rather than crashing.
common never names a resource path as anything but a plain string.
The palettes
| Allele | LUT key | Texture | Look |
|---|---|---|---|
Blupnk | bluepink | textures/coat/lutbluepink.png | The warm red/orange/brown gradient replaced with blue and pink — cooler, dreamier horse colours |
Grnpnk | greenpink | textures/coat/lutgreenpink.png | Green on the black end running to pink on the red one, through a pale seam in the middle — so a would-be black comes out green and a would-be chestnut pink |
More palettes are welcome; each is one more row in LutGene.VARIANTS
(an allele, a LUT key, a 500×500 texture laid out like
redblackgradient.png — left redder, bottom blacker) and the
expression table, carrier wording, founder odds and in-game wiring all follow from
that list. Three other places have to be told, and none of them is Java:
the PNG goes in common/src/main/resources/assets/horsegenetics/textures/coat/,
web/build.gradle.kts copies it to the designer, and
wiki/horse-designer/js/java.js hands its pixels to the wasm with
setAlternateGradient. The offline tools need nothing: they build their LUT
set with LutSet.fromRegistry, which walks the registry the way the game
does.
Drop a gradient in and see the horses the mod bakes against it. Every coat
below comes out of common/ compiled to WebAssembly — the same
bytecode the game runs — with one thing changed: the colour lookup the
composer resolves pigment against is yours. Nothing is
uploaded anywhere; the file never leaves the browser.
What you are looking at
A LUT is a 2D chart, not a ramp. Phase 2 of the coat pipeline arrives at every pixel holding a pair of numbers — how much red pigment survived phase 1, and how much black — and reads the colour off the chart at that coordinate. More red samples further left; more black samples further down. So the bottom-left corner is a true black, the top-left is the reddest chestnut, and the top-right is unpigmented white. Sampling is bilinear, so the chart is a continuous surface rather than a palette of swatches, and a hard edge in the artwork becomes a hard edge on a horse.
Everything a dilution gene does, it does before this lookup. A cream horse is not "a bay with cream painted over it"; it is a bay whose red pigment was turned down, landing somewhere else on the same chart. That is why the dilution rows are the useful ones here: they are the paths across the gradient that the corner colours alone will not tell you about. A palette can have a beautiful black and a beautiful chestnut and still fall apart in the middle, where every buckskin, palomino, champagne and dun in the world lives.
The overlay — what this coat actually reads
The chart is dimmed everywhere the current horse does not sample, so the artwork shows through at full strength exactly where it does, and the brightness of a region is how many texels land there. On top of that sit a dashed bounding box (the range), a solid bar along the bottom and right edges so each axis can be read on its own, and a white crosshair at the centre of mass — where the coat sits on average. The numbers underneath say the same thing twice: once in pigment levels, which is what a gene tunes, and once as chart X/Y, which is where to click in an image editor. Untick Show what this coat reads to see the bare gradient.
This is the part that makes the page useful for tuning something other than a
palette. Switch between Ch/Ch and Cr/Cr on a chestnut and
the crosshair walks right across the chart — champagne to 45%, cremello to
92% — because that is the whole of what a dilution is: the same lookup, read
somewhere else. A gene whose footprint is a single cell is painting one flat colour;
one that spreads over dozens is painting a range, and dun's twenty-nine cells are its
dorsal stripe and leg bars sitting at different depths. If a gene lands somewhere the
gradient has nothing interesting, that shows up here before it shows up on a horse.
The overlay is computed from CoatTextureComposer.pigmentField —
the same phase-1 loop bake runs — so it cannot disagree with the
horse beside it. It deliberately is not derived from the baked sheet,
which could not work: by then the colour has been multiplied by the hair template
and moved again by any phase-3 tint. Two consequences worth knowing. Texels with
no pigment at all are excluded, exactly as phase 2 excludes them, so a
tobiano reads the chart at fewer texels than a solid horse (4 160
against 11 904 on a black) — white is off the chart, not a colour on it.
And a magical gene that paints in phase 3, or an eye colour, will not appear here
at all, because neither one reads the gradient.
Reading a point off the chart
Click anywhere on the gradient and an amber mark pins that spot, with the pigment pair that resolves there: how much red and black is left, and how much a gene would have had to restrict to land on it. The two are complements — 35% restricted is 65% left — and both are shown because a gene's constants are written in what is kept while the thing being reasoned about is usually what was taken away.
The box above it goes the other way. Type a colour
(#B0A08A, B0A08A and #BA8 all work) and it
finds the nearest point on the chart and pins it the same way — so a colour
lifted off reference art becomes the pigment pair a gene would have to leave
behind to hit it. The search runs over whichever gradient is loaded, uploads
included, and it is honest about a miss: a chart that simply does not contain the
colour asked for says so rather than pinning a confident-looking point. Matching
is by redmean
rather than raw RGB distance, which matters on a desaturated chart where most of
the useful range is browns a plain Euclidean metric cannot tell apart.
The amber mark and the white crosshair answer different questions and will often sit near each other: the white one is where the current horse's coat averages out, the amber one is wherever you last pointed.
The rows
Base coat is the three coats every other gene starts from —
black, bay and chestnut, straight from BaseCoats.all(). The
locus rows under it are the five dilution loci and one white
pattern, and they stack: champagne on a cream crosses the chart
differently than either does alone, and a gradient that survives one but not the
other is worth catching. Each row's buttons are whichever combinations the mod
says look different, so a locus that gains an allele gains a button here without
this page being edited.
Cr/prl is drawn with a dashed border
Every other button is a combination the mod itself says looks different.
Cr/prl is not: it shares the double-dilute expression
with Cr/Cr, so the registry collapses the two and a coat preview is
right to. It has a button here anyway because it is a real breeding target —
but measured against the baked sheet, a Cr/prl horse differs from a
Cr/Cr one in exactly eight texels: the two eye
blocks, at MATP's CREAM_PEARL_GREEN (#59A08F). The body
is byte-identical. And that eye colour is a fixed constant rather than a gradient
lookup, so it is the one thing on this page a LUT cannot change. The
dashed border means “this button is here for completeness, not because the
palette does something new to it”.
Only one white pattern is offered, deliberately. Every white locus paints the same thing — unpigmented white, which is off the chart entirely and cannot respond to a palette — so tobiano stands in for all of them and a second one would be a second copy of the same answer. Use it to check that your gradient's whites sit happily next to a real white rather than reading as a dirty patch.
What makes a usable LUT
- Any size.
GradientLutnormalises the coordinates, so a 64×64 chart and a 2048×2048 one both work. The mod's own is 500×500. - Any image the browser can decode — PNG is what the mod
ships and what a gene expects, but the lab will take whatever
<img>accepts. - Corners matter most. Bottom-left is every black horse, top-left every chestnut. The top-right is white and should stay near-white: it is where an unpigmented pixel resolves, and a tinted corner there discolours every horse on the page at once.
- The middle is where the work is. It is where every dilution lands, and it is the part a chart is most likely to get wrong — the default has been rebuilt more than once and the middle changed character each time. Do not take a description of it on trust; switch a dilution on and read the overlay.
Turning one into a real palette
The lab swaps the gradient in this page only — it is a viewer, and
closing the tab is the undo. Shipping a palette to the game is the
LUT locus: one more row in
LutGene.VARIANTS (an allele, a LUT key, and the texture laid out the
way the chart above is), and the expression table, carrier wording and wiring all
follow from that list. There is no per-gene "use this gradient" hook and there is
not going to be one — a horse has two copies of that chromosome and can shift
its palette at most one way at a time.
Replacing the natural gradient instead means editing
redblackgradient.png — and there are two copies of it. The real
one is
common/src/main/resources/assets/horsegenetics/textures/coat/redblackgradient.png;
the one under wiki/horse-designer/assets/ is a copy,
written by :web:bakeDesignerAssets. Edit the wiki's copy and you will
change every horse on every wiki page, change no horse in the game, and lose the
edit the next time anyone re-bakes. Edit the one in common/, then
re-bake.