Coat Color Genetics Calculator for Dogs and Horses

By Dr. Zubair Khalid, DVM, MS, PhD ·

Coat Color Genetics Calculator for Dogs and Horses

By the end of this guide you will have built and run a working coat color genetics calculator in Python. It takes the genotypes of two parents and prints the probability of every coat color outcome in their puppies or foals. You will verify it against known crosses, learn the three errors you are most likely to hit, and see the mistakes that make most homemade calculators misleading.

Have these on hand before you start: Python 3.8 or newer, a plain text editor, and, for predictions about your own animals, the DNA genotypes of both parents from a coat color panel or breeding records. The script uses only the Python standard library, so there is nothing else to install.

A dog coat color calculator is a small biology calculator. It applies Mendelian inheritance to a defined set of loci and converts genotype probabilities into visible phenotypes. The same joint-probability logic, scaled up and fed with sequencing data, becomes the bioinformatics calculator behind forensic panels that predict a dog's appearance from DNA alone [1].

What a Coat Color Genetics Calculator Does

Four terms carry the whole model. A locus is the physical position of a gene on a chromosome. An allele is one variant of the gene at that locus. A genotype is the pair of alleles an individual carries, one inherited from each parent. A phenotype is the visible result, such as a black-and-tan coat.

The calculator automates three jobs.

  1. Punnett squares per locus. For each locus, it pairs every sire gamete with every dam gamete and counts the outcomes, which gives genotype probabilities.
  2. Joint probabilities across loci. Genes on different chromosomes assort independently during meiosis, so the probability of a full genotype combination is the product of the single-locus probabilities.
  3. Epistasis. Epistasis means one gene masks another. In dogs, two copies of the recessive e allele at MC1R produce a red or yellow dog no matter what the animal carries at the black and agouti genes. In horses, one dominant gray allele eventually overrides every base color and dilution.

The output is a probability distribution, not a promise. Each puppy or foal is an independent draw, so a 25 percent outcome can miss a four-offspring litter entirely. A later section shows how to quantify that.

The Dog Locus Model

Five loci cover most single-gene coat color inheritance in dogs. Two pigment molecules run through all of them: eumelanin, the black or brown pigment, and pheomelanin, the red or yellow pigment.

The E locus is MC1R, the switch that decides whether eumelanin can enter the hair shaft. EM adds a dark mask, E is the normal allele, and e is recessive red, which keeps eumelanin out of the coat and leaves a red or yellow dog with a dark or liver nose. Three additional MC1R alleles, eA, eH, and eG, produce domino, grizzle, and sable-type patterns. The eH allele was identified in English Cocker Spaniels, and the updated dominance order is EM > E > eA/eH/eG > e [2].

The K locus is CBD103, a beta-defensin gene. KB is dominant black and overrides the agouti patterns. Wild wolves picked this allele up from domestic dogs through hybridization, and it rose in frequency by natural selection in some North American wolf populations [3]. The kbr allele gives brindle, and ky lets the A locus show through.

The A locus is ASIP, expressed only when K permits. Its order is ay (sable or fawn) > aw (wolf sable) > at (tan points) > a (recessive black). Deep sequencing of 113 Jindo dogs across five color patterns tied the breed's variation to CBD103, ASIP, and MC1R, traced black-and-tan dogs to the island's first major founding population, and showed their closer genetic affinity to ancient western Eurasian wolves [4].

The B locus is TYRP1. B gives black eumelanin, and b/b gives liver, also called brown or chocolate, which also turns the nose and pads liver.

The D locus is MLPH. Two d alleles dilute eumelanin, turning black into blue and liver into lilac or isabella. One MLPH variant, NC_006607.3:g.48121642G>A, segregates in native Korean breeds at frequencies of 0.18 in DongGyeongi and 0.20 in Sapsaree [5].

Depth of red is a separate, multigenic problem. A genome-wide association study of 3,057 pheomelanic dogs from 63 breeds found five loci that explain over 70 percent of pheomelanin intensity variation, which ranges from the white coat of the Samoyed to the deep red of the Irish Setter [6]. One confirmed player is a 6 kb copy number variant 152 kb upstream of KITLG, where higher copy number tracks darker red in Nova Scotia Duck Tolling Retrievers (p = 6.1 × 10⁻⁷) and darker pigment in Poodles and other breeds [7].

LocusGeneAlleles, strongest firstVisible effect
EMC1REM > E > eA, eH, eG > eMask, wild type, domino or grizzle type patterns, recessive red [2]
KCBD103KB > kbr > kyDominant black, brindle, or permission for A-locus patterns [4]
AASIPay > aw > at > aSable, wolf sable, tan points, recessive black [4]
BTYRP1B > bBlack versus liver eumelanin, including nose and pads
DMLPHD > dFull pigment versus blue or lilac dilution [5]
IntensityKITLG copy number plus four GWAS lociCopy number dosage, risk allelesDepth of red and yellow pheomelanin [7], [6]

The Horse Locus Model

Decision path for a horse mating carrying cream, ending in buckskin, palomino, perlino or cremello outcomes.

Horse color starts from the same two genes, MC1R and ASIP, then adds a larger set of dilutions and patterns. Four modeling differences matter.

Cream (CR, SLC45A2) shows incomplete dominance. One copy dilutes red pigment to palomino on a chestnut base or buckskin on a bay base. Two copies dilute further to cremello or perlino. A simple dominant model gets this wrong.

Gray (G, STX17) is dominant and progressive. A gray foal can be born any base color and lightens over years, so the calculator lists gray as its own outcome that masks the base.

Silver (Z, PMEL) dilutes black pigment only. A chestnut horse can carry Z invisibly, which is why hidden silver produces surprises in breeds with many chestnuts.

Frame overo (O, EDNRB) is safe in one copy but lethal in two. A cross of two frame overos can produce a lethal white overo foal, so a responsible calculator flags O x O matings instead of only printing colors.

The remaining textbook loci are D (TBX3) for dun with primitive markings, CH (SLC36A1) for champagne, and TO (KIT) for tobiano spotting.

LocusGeneAllelesVisible effect
EMC1RE > eBlack versus red base pigment
AASIPA > aBay body with black points versus solid black
GSTX17G > gProgressive graying, masks base color with age
CRSLC45A2CR > cr, incomplete dominanceOne copy, buckskin or palomino. Two copies, perlino or cremello
DTBX3D > dDun dilution with dorsal stripe and primitive markings
ZPMELZ > zSilver, dilutes black pigment only, invisible on chestnut
TOKITTO > toTobiano spotting pattern
OEDNRBO > oFrame overo, two copies cause lethal white overo syndrome
CHSLC36A1CH > ch, incomplete dominanceChampagne dilution of both pigments

Build a Dog Coat Color Calculator Step by Step

Work through the seven steps in one file called coat_calc.py. Each block builds on the last, and each one shows the output you should see.

Step 1. Check your Python setup

python3 --version

You should see a line such as:

Python 3.11.4

Any release from 3.8 upward works. Create an empty file named coat_calc.py in your working folder.

Step 2. Define the loci and dominance hierarchies

The order of alleles in each list is the dominance rank, strongest first. This ordering is the most consequential parameter in the whole calculator, and the E-locus order follows the 2024 update.

# Alleles are listed strongest to weakest at each locus.
DOG_LOCI = {
    "E": ["EM", "E", "eA", "eH", "eG", "e"],  # MC1R, extension
    "K": ["KB", "kbr", "ky"],                 # CBD103, dominant black
    "A": ["ay", "aw", "at", "a"],             # ASIP, agouti
    "B": ["B", "b"],                          # TYRP1, brown
    "D": ["D", "d"],                          # MLPH, dilution
}

Each parent enters as a dictionary with exactly two alleles per locus, dominant one first. A panel report that reads E/e at E, KB/ky at K, ay/at at A, B/b at B, and D/D at D becomes {"E": ("E", "e"), "K": ("KB", "ky"), "A": ("ay", "at"), "B": ("B", "b"), "D": ("D", "D")}.

Step 3. Write the single-locus Punnett square

from collections import defaultdict

def punnett(order, a1, a2, b1, b2):
    """One locus. a1 and a2 are the sire alleles, b1 and b2 the dam alleles."""
    counts = defaultdict(int)
    for s in (a1, a2):
        for d in (b1, b2):
            pair = tuple(sorted((s, d), key=order.index))
            counts[pair] += 1
    return {geno: n / 4 for geno, n in counts.items()}

print(punnett(DOG_LOCI["E"], "E", "e", "E", "e"))

Expected output:

{('E', 'E'): 0.25, ('E', 'e'): 0.5, ('e', 'e'): 0.25}

Each of the four gamete pairings contributes a quarter, and sorted() with the dominance index keeps the stronger allele first in every pair.

Step 4. Combine loci into joint probabilities

def joint_cross(sire, dam, loci):
    """Multiply independent single-locus probabilities across loci."""
    joint = {(): 1.0}
    for locus, order in loci.items():
        s1, s2 = sire[locus]
        d1, d2 = dam[locus]
        single = punnett(order, s1, s2, d1, d2)
        nxt = defaultdict(float)
        for prefix, p in joint.items():
            for geno, gp in single.items():
                nxt[prefix + ((locus, geno),)] += p * gp
        joint = dict(nxt)
    return joint

The function starts with an empty genotype at probability 1.0 and multiplies in one locus at a time. The five dog loci sit on different canine chromosomes, so independent assortment holds and the product is exact. If you add loci that sit close together on the same chromosome, replace this step with a linkage-aware model, because recombination rather than independence governs their inheritance.

Step 5. Map genotypes to phenotypes

def a_pattern(A):
    names = {("ay", "ay"): "sable", ("ay", "aw"): "sable",
             ("ay", "at"): "sable", ("ay", "a"): "sable",
             ("aw", "aw"): "wolf sable", ("aw", "at"): "wolf sable",
             ("aw", "a"): "wolf sable",
             ("at", "at"): "tan points", ("at", "a"): "tan points",
             ("a", "a"): "recessive black"}
    return names[A]

def dog_phenotype(g):
    E, K, A, B, D = g["E"], g["K"], g["A"], g["B"], g["D"]
    pigment = "black" if B == "B" else "liver"
    suffix = "" if D == "D" else ", diluted"
    if E == ("e", "e"):
        return f"recessive red, {pigment} nose{suffix}"
    if K == "KB":
        return f"solid {pigment}{suffix}"
    if K == "kbr":
        return f"brindle {a_pattern(A)}, {pigment}{suffix}"
    return f"{a_pattern(A)}, {pigment}{suffix}"

The if-chain is the epistasis hierarchy in code. Recessive red is checked first because e/e masks K and A. Dominant black is checked next because KB masks A. Brindle comes before the plain A patterns. B and D then color the eumelanin and dilute it. One documented simplification applies here: the phenotype step treats eA, eH, and eG like E, while their domino and grizzle patterns need their own rules [2].

Step 6. Run the dog worked example

Both parents look black, yet both carry hidden e, b, and pattern alleles:

sire = {"E": ("E", "e"), "K": ("KB", "ky"), "A": ("ay", "at"),
        "B": ("B", "b"), "D": ("D", "D")}
dam  = {"E": ("E", "e"), "K": ("KB", "ky"), "A": ("at", "at"),
        "B": ("B", "b"), "D": ("D", "D")}

summary = defaultdict(float)
for combo, p in joint_cross(sire, dam, DOG_LOCI).items():
    summary[dog_phenotype(dict(combo))] += p

assert abs(sum(summary.values()) - 1.0) < 1e-9

for pheno, p in sorted(summary.items(), key=lambda kv: (-kv[4], kv)):
    print(f"{pheno:30s}{100 * p:7.2f}%")

Expected output:

solid black                        42.19%
recessive red, black nose          18.75%
solid liver                        14.06%
sable, black                        7.03%
tan points, black                   7.03%
recessive red, liver nose           6.25%
sable, liver                        2.34%
tan points, liver                   2.34%

Two black dogs produce solid black or liver pups only 56 percent of the time. A quarter of the litter is expected to be recessive red, and the ky/ky quarter splits into sable and tan points. The assert line confirms the probabilities sum to 1, and the printed values sum to 100 with rounding.

Step 7. Run the horse worked example

The same engine runs horse loci. Add:

HORSE_LOCI = {
    "E":  ["E", "e"],    # MC1R, extension
    "A":  ["A", "a"],    # ASIP, agouti
    "CR": ["CR", "cr"],  # SLC45A2, cream, incomplete dominance
    "G":  ["G", "g"],    # STX17, gray
}

CREAM = {"bay": {"single": "buckskin", "double": "perlino"},
         "black": {"single": "smoky black", "double": "smoky cream"},
         "chestnut": {"single": "palomino", "double": "cremello"}}

def horse_phenotype(g):
    E, A, CR, G = g["E"], g["A"], g["CR"], g["G"]
    if G == "G":
        return "gray (lightens with age)"
    base = "chestnut" if E == ("e", "e") else ("bay" if A == "A" else "black")
    if CR == ("CR", "CR"):
        return CREAM[base]["double"]
    if CR == ("CR", "cr"):
        return CREAM[base]["single"]
    return base

mare     = {"E": ("E", "e"), "A": ("A", "a"), "CR": ("CR", "cr"), "G": ("g", "g")}
stallion = {"E": ("e", "e"), "A": ("A", "a"), "CR": ("CR", "cr"), "G": ("g", "g")}

summary = defaultdict(float)
for combo, p in joint_cross(mare, stallion, HORSE_LOCI).items():
    summary[horse_phenotype(dict(combo))] += p

for pheno, p in sorted(summary.items(), key=lambda kv: (-kv[4], kv)):
    print(f"{pheno:26s}{100 * p:7.2f}%")

Expected output:

palomino                   25.00%
buckskin                   18.75%
chestnut                   12.50%
cremello                   12.50%
bay                         9.38%
perlino                     9.38%
smoky black                 6.25%
black                       3.13%
smoky cream                 3.13%

The mare is a buckskin and the stallion is a palomino, so both carry cream. Their foals get one cream dose half the time and two doses a quarter of the time, which is why cream-diluted colors dominate the list. Neither parent carries gray, so no gray appears. If either parent were G/g, every gray foal would print as gray, because gray overrides the base.

Checking Your Results

Run these test crosses before you trust the calculator on real data.

Test crossExpected output
e/e x e/e at the E locus100 percent recessive red
KB/KB x ky/ky at the K locus100 percent KB/ky, solid eumelanin
b/b x b/b at the B locus100 percent liver-based eumelanin
Horse E/e A/a x e/e a/a25 percent bay, 25 percent black, 50 percent chestnut

Then check sampling. A 25 percent chance per offspring does not mean one red puppy in every four:

p_red = 0.25
litter = 4
print(f"No red puppy in a litter of {litter}: {100 * (1 - p_red) ** litter:.1f}%")
No red puppy in a litter of 4: 31.6%

Three tips keep results honest. First, record which alleles your DNA panel actually tests, because alleles described recently, such as eH in 2024, may be absent from older panels. Second, keep the dominance lists as the single source of truth and import them into every script rather than retyping them. Third, keep the assert line in place so a broken edit fails loudly instead of printing probabilities that do not sum to 1.

Three errors you will hit and their fixes

  • ValueError: 'Eb' is not in list, raised by order.index. An allele is misspelled or missing from the hierarchy. Fix the spelling against the locus table.
  • KeyError: 'D', raised in joint_cross. A parent dictionary is missing a locus. Every parent needs a two-allele entry for every locus in the model.
  • ValueError: too many values to unpack (expected 2), raised at s1, s2 = sire[locus]. A genotype has more than two alleles. Diploid means exactly two.

Common Mistakes and Limitations

Four warning cards covering E locus order, epistasis, guessing genotypes from looks and horse lethality.

  1. Misordering the E-locus hierarchy. Putting e above the domino-type alleles, or treating eA, eH, and eG as full loss-of-function variants, breaks pattern predictions in breeds such as the English Cocker Spaniel. Keep the published order, EM above E above eA/eH/eG above e.
  2. Averaging loci instead of conditioning on epistasis. Computing the K-locus ratio and the E-locus ratio separately and combining phenotype labels by hand double counts outcomes. The joint table plus one phenotype function that checks e/e first, then KB, then kbr, then A, keeps the arithmetic honest.
  3. Reading phenotype as genotype. Two black dogs can be KB/KB or KB/ky at K, and E/E or E/e at E. Guessing genotypes from looks hides exactly the alleles that produce surprises like recessive red. DNA-tested genotypes remove the guesswork.
  4. Ignoring dosage and lethality in horses. Cream is a dosage system, not a simple dominant. Frame overo is worse than a color mistake, because two O alleles can produce a lethal white overo foal. A calculator that treats O like an ordinary dominant allele is unsafe for breeding decisions.
  5. Assuming color predicts behavior. In a C-BARQ study of 215 Australian Shepherds, coat color was not associated with generalized behavioral tendencies, while sex and neuter status were far stronger predictors [8]. Build behavior expectations on behavior data, not on the merle allele.
  6. Selecting breeding pairs by color alone. Breeding for coat colors and spotting patterns in the endangered Majorcan Ca Mè dog split the population into color-based subpopulations with inbreeding coefficients ranging from 6.3 to 10.4 percent within color groups, and the authors recommend choosing pairs with a relatedness coefficient below 15 percent [9]. A calculator that only prints colors cannot see this cost, so pair it with pedigree or genomic relatedness data.
  7. Expecting exact shade prediction. Single-gene loci predict a category, not a depth of pigment. Intensity depends on at least five loci [6] and on copy number at KITLG [7], so treat red outputs as a range from white to deep red.
  8. Forgetting pleiotropy and linkage. Coat color genes touch other traits. In Jindo dogs, the black-and-tan alleles significantly decrease body size [4]. In Labrador Retrievers, yellow coat color was associated with increased risk of anterior cruciate ligament rupture, and the underlying variants may be linked to color genes [10]. In wolves, KB homozygotes show lower fitness than heterozygotes despite identical black coats, which points to pleiotropic effects, although one experiment on keratinocyte immune responses found no transcriptional differences by K genotype [11].

This model is deliberately simplified. It omits white spotting (MITF), merle (PMEL), and hair length, and it treats dilution as a single switch. Merle deserves special caution, because breeding two merle dogs can produce double merle puppies with serious eye and ear defects. For real breeding decisions, genotype both parents at every locus you care about, and for any health question about an individual animal, work with a veterinarian.

From Punnett Squares to DNA Phenotyping

The calculator you built is the deterministic end of a spectrum. At the other end, forensic teams predict appearance from crime-scene DNA. The LASSIE MPS panel uses 44 genetic markers in a single massively parallel sequencing assay and a naive Bayes classifier to predict coat color, coat pattern, coat structure, tail morphology, skull shape, ear shape, eye color, and body size, with very high classification success for some trait categories and successful blind predictions on test samples [1].

The shared idea is the one you implemented: turn genotype into phenotype probability through an explicit model. Sensible next steps are adding the S and M loci with their real allele systems, replacing the phenotype lookup with a Bayesian model that carries uncertainty, and validating predictions against your breed club's litter records. Each step moves the same biology calculator toward a proper bioinformatics calculator.

Frequently Asked Questions

How accurate is a dog coat color calculator?

Very accurate for single-gene loci when both parents have DNA-tested genotypes. Accuracy drops when parents are untested carriers or when the trait depends on many genes, as red intensity does.

Can two black dogs produce red or yellow puppies?

Yes. When both parents carry one recessive e allele at the E locus, each puppy has a 25 percent chance of being recessive red.

What do the E, K, and A loci control in dogs?

The E locus (MC1R) decides whether black pigment can enter the coat, the K locus (CBD103) can override the pattern genes with solid black or brindle, and the A locus (ASIP) sets the pattern, such as sable or tan points, when K allows it.

Do I need DNA test results before using the calculator?

DNA-tested genotypes for both parents give the most reliable predictions, because two dogs with the same visible color can carry different hidden alleles.

Can the calculator predict merle and white spotting?

Only in simplified form. Merle and white spotting involve complex allele systems, and merle-to-merle breedings can produce double merle puppies with serious eye and ear defects, so those crosses need expert guidance.

What is the difference between a buckskin and a perlino horse?

Both are bay-based horses with the cream dilution. A buckskin carries one CR allele and a perlino carries two.

Does coat color predict temperament or health?

Color alone does not predict behavior. Australian Shepherds showed no association between coat color and generalized behavioral tendencies in a validated questionnaire study, although some color loci sit near genes tied to other traits.

Why did my litter not match the calculator predictions?

Probabilities describe each individual offspring, not a guaranteed ratio. A 25 percent outcome can miss a four-puppy litter entirely, and observed ratios converge on predictions only as the number of offspring grows.

Sources

  1. The LASSIE MPS panel: Predicting externally visible traits in dogs for forensic purposes.
  2. Canine coat color E locus updates: Identification of a new MC1R variant causing 'sable' coat color in English Cocker Spaniels and a proposed update to the E locus dominance hierarchy.
  3. EPAS1 variants in high altitude Tibetan wolves were selectively introgressed into highland dogs.
  4. Deep sequencing of Korean Jindo dog reveals evolutionary trajectory of coat color variations.
  5. PCR-based detection of hereditary mutations in SLC2A9, BTBD17, and NECAP1 among native Korean dog breeds.
  6. Five genetic variants explain over 70% of hair coat pheomelanin intensity variation in purebred and mixed breed domestic dogs.
  7. Pigment Intensity in Dogs is Associated with a Copy Number Variant Upstream of KITLG.
  8. Behavior in Australian Shepherd Dogs Assessed Using the C-BARQ: A Preliminary Study of Associations with Coat Color, Sex, and Neutering Status.
  9. Impact of breeding for coat and spotting patterns on the population structure and genetic diversity of an islander endangered dog breed.
  10. Identification of genetic variants associated with anterior cruciate ligament rupture and AKC standard coat color in the Labrador Retriever.
  11. K Locus Effects in Gray Wolves: Experimental Assessment of TLR3 Signaling and the Gene Expression Response to Canine Distemper Virus.