#punnett-square-calculator { max-width: 600px; margin: 20px auto; padding: 15px; background: #fff; border-radius: 5px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); font-family: -apple-system, BlinkMacSystemFont, ‘Arial’, sans-serif; color: #333; box-sizing: border-box; } h2 { text-align: center; margin: 0 0 15px; font-size: 1.4rem; line-height: 1.2; color: #333; } p { text-align: center; color: #666; font-size: 14px; margin-bottom: 15px; } .form-group { margin-bottom: 15px; position: relative; } label { display: block; margin-bottom: 5px; font-size: 0.9rem; font-weight: 600; } input[type=”text”], select { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 3px; font-size: 0.9rem; box-sizing: border-box; } select:focus, input:focus { border-color: #0073aa; outline: none; } .tooltip { position: relative; display: inline-block; margin-left: 5px; } .tooltip .tooltip-text { visibility: hidden; width: 180px; background: #333; color: #fff; text-align: center; border-radius: 3px; padding: 5px; position: absolute; z-index: 10; bottom: 125%; left: 50%; transform: translateX(-50%); opacity: 0; transition: opacity 0.3s; font-size: 0.75rem; } .tooltip:hover .tooltip-text { visibility: visible; opacity: 1; } .button-group { display: flex; gap: 10px; justify-content: center; margin-top: 15px; } button { padding: 8px 16px; border: none; border-radius: 3px; cursor: pointer; font-size: 0.9rem; background: #0073aa; color: #fff; } button:hover { background: #005177; } #reset-btn { background: #6c757d; } #reset-btn:hover { background: #5a6268; } #copy-btn { background: #28a745; } #copy-btn:hover { background: #218838; } #result { margin-top: 15px; padding: 10px; border-radius: 3px; font-size: 0.9rem; line-height: 1.4; min-height: 20px; } #result.success { background: #e6f4ea; border: 1px solid #28a745; } #result.error { background: #f8d7da; border: 1px solid #dc3545; } .punnett-table { width: 100%; border-collapse: collapse; margin-top: 10px; } .punnett-table th, .punnett-table td { padding: 8px; border: 1px solid #ddd; text-align: center; font-size: 0.85rem; } .punnett-table th { background: #f8f9fa; font-weight: 600; } .punnett-table td.dominant { background: #d4edda; } .punnett-table td.recessive { background: #f8d7da; } .ratio-table { width: 100%; border-collapse: collapse; margin-top: 10px; } .ratio-table th, .ratio-table td { padding: 8px; border: 1px solid #ddd; text-align: left; font-size: 0.85rem; } .ratio-table th { background: #f8f9fa; font-weight: 600; } #trait2-group { display: none; } noscript { display: block; color: #dc3545; text-align: center; margin-top: 10px; font-size: 0.9rem; } @media (max-width: 600px) { #punnett-square-calculator { margin: 10px; padding: 10px; } h2 { font-size: 1.2rem; } .button-group { flex-direction: column; } button { width: 100%; } .punnett-table th, .punnett-table td { font-size: 0.7rem; padding: 4px; } .ratio-table th, .ratio-table td { font-size: 0.8rem; padding: 6px; } }

Enhanced Punnett Square Calculator

Calculate genotype and phenotype ratios for monohybrid or dihybrid crosses.

Monohybrid (1 Trait) Dihybrid (2 Traits)
AA (Homozygous Dominant) Aa (Heterozygous) aa (Homozygous Recessive)
AA (Homozygous Dominant) Aa (Heterozygous) aa (Homozygous Recessive)
BB (Homozygous Dominant) Bb (Heterozygous) bb (Homozygous Recessive)
BB (Homozygous Dominant) Bb (Heterozygous) bb (Homozygous Recessive)
JavaScript is disabled. Please enable it to use the calculator. (function() { if (!document.getElementById(‘punnett-square-calculator’)) return; const form = document.getElementById(‘calc-form’); const crossTypeSelect = document.getElementById(‘cross-type’); const trait1NameInput = document.getElementById(‘trait1-name’); const trait2NameInput = document.getElementById(‘trait2-name’); const parent1Trait1Select = document.getElementById(‘parent1-trait1’); const parent2Trait1Select = document.getElementById(‘parent2-trait1’); const parent1Trait2Select = document.getElementById(‘parent1-trait2’); const parent2Trait2Select = document.getElementById(‘parent2-trait2’); const trait2Group = document.getElementById(‘trait2-group’); const resultDiv = document.getElementById(‘result’); function toggleInputs() { const isDihybrid = crossTypeSelect.value === ‘dihybrid’; trait2Group.style.display = isDihybrid ? ‘block’ : ‘none’; if (!isDihybrid) { trait2NameInput.value = ”; parent1Trait2Select.value = ‘BB’; parent2Trait2Select.value = ‘BB’; } } crossTypeSelect.addEventListener(‘change’, toggleInputs); toggleInputs(); function getGametes(genotype) { const alleles = genotype.split(”); return alleles.length === 2 ? alleles.sort() : [alleles[0]]; } function combineTraits(trait1, trait2) { const result = []; for (let a1 of trait1) { for (let a2 of trait2) { result.push(a1 + a2); } } return result.sort(); } function getSortedGenotype(g1, g2, isTrait2 = false) { const offset = isTrait2 ? 2 : 0; const trait = [g1[offset], g2[offset]].sort().join(”); if (isTrait2) { const trait2 = [g1[1], g2[1]].sort().join(”); return trait + trait2; } return trait; } function getPhenotype(genotype, traitName, isDihybrid = false, trait2Name = ”) { if (isDihybrid) { const trait1 = genotype.slice(0, 2); const trait2 = genotype.slice(2, 4); const pheno1 = /[A]/.test(trait1) ? `Dominant ${traitName}` : `Recessive ${traitName}`; const pheno2 = /[B]/.test(trait2) ? `Dominant ${trait2Name}` : `Recessive ${trait2Name}`; return `${pheno1}, ${pheno2}`; } return /[A]/.test(genotype) ? `Dominant ${traitName}` : `Recessive ${traitName}`; } function calculate() { resultDiv.className = ”; resultDiv.innerHTML = ”; try { const isDihybrid = crossTypeSelect.value === ‘dihybrid’; const trait1Name = trait1NameInput.value.trim() || ‘Trait 1’; const trait2Name = trait2NameInput.value.trim() || ‘Trait 2’; const parent1Trait1 = parent1Trait1Select.value; const parent2Trait1 = parent2Trait1Select.value; const parent1Trait2 = isDihybrid ? parent1Trait2Select.value : ‘BB’; const parent2Trait2 = isDihybrid ? parent2Trait2Select.value : ‘BB’; if (!trait1Name) throw new Error(‘Trait 1 name is required.’); if (isDihybrid && !trait2Name) throw new Error(‘Trait 2 name is required.’); if (!parent1Trait1 || !parent2Trait1 || (isDihybrid && (!parent1Trait2 || !parent2Trait2))) { throw new Error(‘All genotypes must be selected.’); } let parent1Gametes, parent2Gametes, total; const genotypes = {}; const phenotypes = {}; if (isDihybrid) { const gametes1Trait1 = getGametes(parent1Trait1); const gametes1Trait2 = getGametes(parent1Trait2); const gametes2Trait1 = getGametes(parent2Trait1); const gametes2Trait2 = getGametes(parent2Trait2); parent1Gametes = combineTraits(gametes1Trait1, gametes1Trait2); parent2Gametes = combineTraits(gametes2Trait1, gametes2Trait2); total = 16; } else { parent1Gametes = getGametes(parent1Trait1); parent2Gametes = getGametes(parent2Trait1); total = parent1Gametes.length * parent2Gametes.length; } const punnettData = []; for (let g1 of parent1Gametes) { const row = []; for (let g2 of parent2Gametes) { const genotype = isDihybrid ? getSortedGenotype(g1, g2, true) : getSortedGenotype(g1, g2); genotypes[genotype] = (genotypes[genotype] || 0) + 1; const phenotype = getPhenotype(genotype, trait1Name, isDihybrid, trait2Name); phenotypes[phenotype] = (phenotypes[phenotype] || 0) + 1; row.push({ genotype, phenotype }); } punnettData.push(row); } let tableHTML = ` `; for (let g2 of parent2Gametes) { tableHTML += ``; } tableHTML += ‘‘; for (let i = 0; i < parent1Gametes.length; i++) { tableHTML += ``; for (let cell of punnettData[i]) { const cellClass = cell.phenotype.includes(‘Recessive’) ? ‘recessive’ : ‘dominant’; tableHTML += ``; } tableHTML += ‘‘; } tableHTML += ‘
${isDihybrid ? g2 : g2[0]}
${isDihybrid ? parent1Gametes[i] : parent1Gametes[i][0]}${cell.genotype}
‘; let genotypeHTML = ` `; for (let genotype in genotypes) { genotypeHTML += ` `; } genotypeHTML += ‘
GenotypeRatio
${genotype} ${genotypes[genotype]}/${total} (${(genotypes[genotype] / total * 100).toFixed(2)}%)
‘; let phenotypeHTML = ` `; for (let phenotype in phenotypes) { phenotypeHTML += ` `; } phenotypeHTML += ‘
PhenotypeRatio
${phenotype} ${phenotypes[phenotype]}/${total} (${(phenotypes[phenotype] / total * 100).toFixed(2)}%)
‘; let equilibriumNote = ”; if (isDihybrid && parent1Trait1 === ‘Aa’ && parent1Trait2 === ‘Bb’ && parent2Trait1 === ‘Aa’ && parent2Trait2 === ‘Bb’) { const observedRatios = Object.values(phenotypes).sort((a, b) => b – a); equilibriumNote = observedRatios.join(‘:’) === ‘9:3:3:1’ ? `
Hardy-Weinberg Check: Matches expected 9:3:3:1 ratio for a standard dihybrid cross.` : `
Hardy-Weinberg Check: Deviates from expected 9:3:3:1 ratio.`; } resultDiv.innerHTML = ` Punnett Square:
${tableHTML}
Genotype Ratios:
${genotypeHTML}
Phenotype Ratios:
${phenotypeHTML} ${equilibriumNote}
`; resultDiv.className = ‘success’; document.getElementById(‘copy-btn’).addEventListener(‘click’, () => { const text = ` Punnett Square Results Cross Type: ${isDihybrid ? ‘Dihybrid’ : ‘Monohybrid’} Trait 1: ${trait1Name} ${isDihybrid ? `Trait 2: ${trait2Name}\n` : ”} Parent 1 Genotype: ${parent1Trait1}${isDihybrid ? parent1Trait2 : ”} Parent 2 Genotype: ${parent2Trait1}${isDihybrid ? parent2Trait2 : ”} Genotype Ratios: ${Object.entries(genotypes).map(([g, c]) => `${g}: ${c}/${total}`).join(‘\n’)} Phenotype Ratios: ${Object.entries(phenotypes).map(([p, c]) => `${p}: ${c}/${total}`).join(‘\n’)} ${equilibriumNote ? `Interpretation:\n${equilibriumNote.replace(‘
Hardy-Weinberg Check: ‘, ”)}` : ”} `.trim(); navigator.clipboard.writeText(text).then(() => { alert(‘Results copied to clipboard!’); }); }); } catch (error) { resultDiv.innerHTML = `Error: ${error.message}`; resultDiv.className = ‘error’; } } function reset() { form.reset(); toggleInputs(); resultDiv.className = ”; resultDiv.innerHTML = ‘Select cross type, enter names, and genotypes, then click “Calculate”.’; crossTypeSelect.focus(); } document.getElementById(‘calculate-btn’).addEventListener(‘click’, calculate); document.getElementById(‘reset-btn’).addEventListener(‘click’, reset); form.addEventListener(‘keypress’, function(e) { if (e.key === ‘Enter’ && e.target.tagName !== ‘BUTTON’) { e.preventDefault(); calculate(); } }); })();