Natural gene / phase 1
Agouti — the four bays
A restricts black toward the points. How far it restricts is the shade locus’s business, and that is what makes one A-carrying horse a bright blood bay and the next a near-black seal brown. Two alleles here; four bays out.
What it does
What to look for: a horse with a red or brown body and black points — black mane, tail and lower legs. That is a bay. Without this gene a black-pigmented horse is black all over.
The catch: how dark a bay comes out is not decided here. Two horses with identical agouti can be a bright red bay and a nearly-black seal brown, because shade is what settles that.
It does nothing at all on a chestnut, which carries it invisibly and passes it on.
Crossing two of them
Its gene carrot
- Gene key
- horsegenetics.agouti
- Alleles
- A a
- Outcomes
- wild,
blood-bay,bay,liver-bay,seal-brown - Shows when
A/a,A/A— and only on a horse that makes black- Reads
- horsegenetics.shade, via
coatDependsOn() - Default allele
- a
- Wild frequency
- 50/50 per allele
- Founder draws
- 1 × nextFloat()
- Deterministic
- no when expressed
- Epigenetic draws
- 5 × nextFloat
- Generator
- coat.pattern.BayCoat, genetics.BayShade
Agouti’s mechanism was owner-verified in-game on 2026-09-01: the points render, the leg and face fade reads right, and the dilutions scale the points. All of that survives unchanged. What is new and unplayed is everything the shade score moves — the body ramp, the four labels and the seal’s soft points. It has been checked against baked sheets and side views, not in the game. See verification.
What the shade score paints
BayShade.spread() rescales the score to 0
(black held to the points, reddest body) to 1 (black over nearly
all of it). BayCoat reads that one number and moves four things.
| Body | BayCoat.bodyBlack(spread) — from BODY_BLACK_LIGHT to BODY_BLACK_DARK on a BODY_CURVE power ramp. This is the term that makes a liver bay a liver bay, and the one the old constant-body bay had no way to say at all. |
|---|---|
| Mane, tail, ears | Full black at every shade — setBlack(1), setRed(0). Every bay has black points; that is what makes it a bay. |
| Hooves | Always solidly black for HOOF_FRACTION of leg height. |
| Legs | legHeight(spread), then an independent jitter per leg, fading out at the top. |
| Face | faceHeight(spread) — squared, so the face only climbs on horses whose legs already did. You get a seal brown, not “socks plus a black face”. |
| Soft points | softPoints(spread), zero until the dark end. See below. |
The body ramp’s middle is the horse that was already right
The old bay had one body-black constant, 0.32, and it was
verified in-game. BODY_CURVE is chosen so that
bodyBlack(0.5) comes back to exactly that — the middle of
the new range is the coat that was already known to be good, and only the two
ends are new. BayShadeGeneTest asserts it, so the constant cannot
drift out from under the claim.
The soft points — what makes a seal a seal
A seal brown is not “a bay whose black climbed high”, which is what the old version drew. It is a nearly black horse with lighter areas left — a mealy muzzle, a paler ring over the eye, and soft tan at the elbow and the stifle. Drawn without them the dark end of the range is just a black horse with a slightly warm cast, and the single most identifiable thing about the phenotype is missing.
- Muzzle. Strongest at the nose, tapering to
MUZZLE_BACKwhere it meets the head, so the mealiness runs out instead of stopping at the box edge. It runs after the face ramp, so it takes the muzzle back off it — an ordinary bay’s muzzle is black and a seal’s is tan. - Over the eye. The forehead-centred falloff
dun’s face mask already uses, at
SOFT_EYE_REACH. - Elbow and stifle. Two broad, low, overlapping fields on the barrel — deliberately broad: drawn small they read as a pair of orange freckles rather than as a light underside.
- The inner limb, added with
CoatRegions.medial. The outside of a seal brown’s legs is black; it is the inner surface of the thigh and forearm that keeps the tan. It went undrawn for a while because “the inside of a leg” is a face of a part rather than a part, which nothing here could address.
The five draws
double spread = BayShade.spread(ctx.genotype(), epi); // 1 - the expression roll
double leg = legHeight(spread);
for (int i = 0; i < legs.length; i++) { // 4 - one jitter per leg
legs[i] = leg * (1.0 - LEG_JITTER + epi.nextFloat() * LEG_JITTER * 2.0);
}
The order is a contract: the golden coats and every “adding a gene
shifts the stream by this much” claim lean on it, and
BayShadeGeneTest pins the count.
The fade, and the hoof floor
The bottom SOLID_PORTION of each leg band is solid black; the
rest is a smoothstep fade to nothing. Smoothstep has a flat
slope at both ends, so the black dissolves into the body colour with no
visible cut-off line. (Splash’s socks
do have a visible cut-off line, and that is the open issue there.)
The hoof guarantee used to raise the whole band to
HOOF_FRACTION / SOLID_PORTION = 0.4, so every leg shorter
than that came out at exactly 0.4 — the per-leg jitter vanished on
any bay whose black did not already climb past the knee, and four
identical socks is the one thing the jitter exists to prevent. It floors
the solid portion now, which keeps the guarantee and leaves the
jitter underneath it. Blood bays are common enough in the new
distribution that this stopped being a corner case.
Bay sets pigment absolutely — and why that matters
BayCoat paints its points absolutely:
setBlack(1.0), setRed(0.0), via
CoatRegions.blackenPart. Agouti runs before
cream, pearl and champagne in Genes.naturalOrder(), so the
dilutions do run after and do scale the points.
What broke, once, was the gradient, not the ordering. With
red = 0 the gradient’s zero-red column stays visually jet
black all the way down to black ≈ 0.4, so scaling black alone
moved the sample without changing the colour: single cream’s
black *= 0.7 landed on #111111, double pearl on
#272727. Every “diluted” bay still had jet points.
The fix was PigmentField.dilute,
which feeds a fraction of the removed eumelanin back in as pheomelanin and
walks the sample sideways off that column into the warm browns. The ordering
and the absolute bay point both stayed as they were.
The soft points are not points in that sense — they end
with red ≈ 1, so the black × (1 - red) test a
dilution uses to spare a point reads them as body coat, and dilutes them.
That is correct: a seal brown’s mealy muzzle is body colour, not a black
point, and a silver or dun seal should dilute it.
A bay carrying champagne, cream or pearl shows real diluted points — amber champagne chocolate over gold, buckskin dark brown over gold, perlino rusty, pearl bay sepia — instead of the jet black they all rendered before. That was verified against the constant-body bay; the points are unchanged by this overhaul, the body under them is not.
Interactions
- Invisible on a chestnut. No black to restrict — a
chestnut can carry
A, and a whole shade genotype with it, silently for generations. - The dilutions decide the colour of the points; agouti still decides where they are — and shade decides how much body is left for the dilution to work on, so a cream over a liver bay and a cream over a blood bay are different buckskins.
- Grey reads bay’s output like any other pigment, and its point-retention knob keeps mane, tail and lower legs darker on the least-greyed horses — which reads as a young grey that used to be a bay.
- Seal brown and black are meant to be confusable. They are in real life; the muzzle and the elbow are what tell them apart, in the game as in a field.
Knobs
Values live in BayCoat and BayShade and are
deliberately not repeated here — a number written into prose goes stale
the next time the code moves. What each one means:
| Constant | Meaning |
|---|---|
BayCoat.BODY_BLACK_LIGHT / _DARK | The two ends of the body ramp: reddest bay, and near-black seal. |
BayCoat.BODY_CURVE | Shape of that ramp. Pinned so bodyBlack(0.5) is the verified 0.32. |
BayCoat.HOOF_FRACTION | Always-black bottom of each leg. |
BayCoat.SOLID_PORTION | Fraction of the point band that is solid before the fade. |
BayCoat.LEG_MIN / LEG_RANGE / LEG_JITTER | Leg-black height from the spread, and the per-leg variation around it. |
BayCoat.FACE_MIN / FACE_RANGE | Face-black height, on the squared curve. |
BayCoat.SOFT_START / SOFT_FULL / SOFT_BLACK | Where the seal’s tan begins, where it reaches full, and how light it goes. |
BayCoat.SOFT_EYE_REACH / MUZZLE_BACK | Size of the pale ring over the eye; how much mealiness survives at the back of the muzzle. |
BayShade.* | The score: the two dosage bonuses, the roll width, and the three band edges. See shade. |
Why four outcomes and not one
Blood bay, bay, liver bay and seal brown are
phenotype descriptions, not loci. Every one of them tests as
the same foundation — one functional MC1R copy and one
functional ASIP copy, E_A_ — and no validated
equine allele separates them. What separates them in a real horse is a
regulatory region, a couple of dosage effects and a polygenic tail, and what
separates them here is the same list, summed into
one shade score.
So agouti declares four bay Expressions and picks between them in
expressionIn, by reading the rest of the genotype. Its
pair-only expressionOf answers bay for any
A horse and admits it is coarse — the
same position the leopard complex is in, and
the reason both genes list their combinations in the preview window
rather than their outcomes.
At
A seal brown is ShD/ShD plus a dosage term, not an allele of
this gene. The A+ / A / At /
a series people quote for seal brown is real in other
mammals and unvalidated in horses; the
shade page has the argument and the alternative designs that were
rejected.
common/genetics/genes/AgoutiGene.java,
common/coat/pattern/BayCoat.java,
common/genetics/BayShade.java