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-carrier and greenpink-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 green/pink palette is not yet play-tested

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

CombinationOutcomeEffect
n/nwildThe natural red/black gradient
Blupnk/nbluepink-carrierNothing — a silent carrier, passes the allele on
Blupnk/BlupnkbluepinkEvery melanin gene resolves against lutbluepink.png
Grnpnk/ngreenpink-carrierNothing — a silent carrier, passes the allele on
Grnpnk/GrnpnkgreenpinkEvery melanin gene resolves against lutgreenpink.png
Blupnk/GrnpnkwildNothing. 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

AlleleLUT keyTextureLook
Blupnkbluepinktextures/coat/lutbluepink.pngThe warm red/orange/brown gradient replaced with blue and pink — cooler, dreamier horse colours
Grnpnkgreenpinktextures/coat/lutgreenpink.pngGreen 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.

It is phase 1, not the finished pixels

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.

Why 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. GradientLut normalises 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.