Antibiotic Resistance & Natural Selection Simulator
:root {
–bg: #f8fafc;
–card: #ffffff;
–border: #e2e8f0;
–text: #1e293b;
–susceptible: #10b981;
–resistant: #ef4444;
}
body {
font-family: system-ui, -apple-system, sans-serif;
margin: 0;
padding: 20px;
background: var(–bg);
color: var(–text);
display: flex;
flex-direction: column;
align-items: center;
}
.container {
max-width: 900px;
width: 100%;
background: var(–card);
border-radius: 12px;
border: 1px solid var(–border);
padding: 24px;
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.05);
}
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 20px;
}
@media (max-width: 700px) {
.grid { grid-template-columns: 1fr; }
}
.dish-container {
display: flex;
flex-direction: column;
align-items: center;
}
canvas {
border: 2px solid var(–border);
border-radius: 8px;
background: #0f172a;
width: 100%;
max-width: 380px;
height: 280px;
}
.controls {
background: #f1f5f9;
padding: 16px;
border-radius: 8px;
display: flex;
flex-direction: column;
gap: 12px;
}
button {
background: #2563eb;
color: white;
border: none;
padding: 10px 16px;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
button:hover { background: #1d4ed8; }
button.secondary { background: #64748b; }
button.secondary:hover { background: #475569; }
.btn-row { display: flex; gap: 8px; }
label { font-weight: 600; font-size: 0.9rem; }
select, input[type=”range”] {
width: 100%;
padding: 6px;
border-radius: 6px;
border: 1px solid var(–border);
}
.stats {
display: flex;
justify-content: space-around;
margin-top: 10px;
font-weight: 600;
font-size: 0.95rem;
}
.stat-susceptible { color: var(–susceptible); }
.stat-resistant { color: var(–resistant); }
Antibiotic Resistance Simulator
Observe how dosage and patient adherence shape natural selection over generations.
● Resistant: 5
Day: 0
Full 7-Day Course (Follows Prescription)
Stops Early on Day 3 (Felt Better)
Missed Doses (Intermittent)
No Antibiotics (Fitness Cost Test)
const canvas = document.getElementById(‘dishCanvas’);
const ctx = canvas.getContext(‘2d’);
let day = 0;
let bacteria = [];
let timer = null;
function init() {
day = 0;
bacteria = [];
// 95 susceptible, 5 resistant
for (let i = 0; i < 95; i++) {
bacteria.push({ type: 'susceptible', x: Math.random() * 340 + 20, y: Math.random() * 240 + 20 });
}
for (let i = 0; i 3) activeDose = 0;
if (regimen === ‘intermittent’ && day % 2 === 0) activeDose = 0;
// 1. Elimination phase
bacteria = bacteria.filter(b => {
if (b.type === ‘susceptible’) {
const killProb = activeDose / 100;
return Math.random() > killProb;
} else {
// Resistant bacteria survive unless dose is overwhelming
const killProb = (activeDose – 60) / 100;
return Math.random() > Math.max(0, killProb);
}
});
// 2. Growth / Reproduction phase (Limited carrying capacity = 160)
const capacity = 160;
const currentCount = bacteria.length;
if (currentCount 0) {
const growthRoom = capacity – currentCount;
const newBacteria = [];
bacteria.forEach(b => {
// Fitness cost: Susceptible divide faster when antibiotic is low
let reproRate = b.type === ‘susceptible’ ? 0.6 : 0.35;
if (Math.random() < reproRate && (newBacteria.length + bacteria.length) {
ctx.beginPath();
ctx.arc(b.x, b.y, 4.5, 0, Math.PI * 2);
ctx.fillStyle = b.type === ‘susceptible’ ? ‘#10b981’ : ‘#ef4444’;
ctx.fill();
});
}
function updateUI() {
const susc = bacteria.filter(b => b.type === ‘susceptible’).length;
const res = bacteria.filter(b => b.type === ‘resistant’).length;
document.getElementById(‘countSusceptible’).innerText = susc;
document.getElementById(‘countResistant’).innerText = res;
document.getElementById(‘dayCount’).innerText = day;
}
document.getElementById(‘btnStep’).addEventListener(‘click’, stepDay);
document.getElementById(‘btnReset’).addEventListener(‘click’, () => {
clearInterval(timer);
timer = null;
document.getElementById(‘btnAuto’).innerText = ‘Auto Run’;
init();
});
document.getElementById(‘btnAuto’).addEventListener(‘click’, () => {
if (timer) {
clearInterval(timer);
timer = null;
document.getElementById(‘btnAuto’).innerText = ‘Auto Run’;
} else {
document.getElementById(‘btnAuto’).innerText = ‘Pause’;
timer = setInterval(() => {
if (day >= 12 || bacteria.length === 0) {
clearInterval(timer);
timer = null;
document.getElementById(‘btnAuto’).innerText = ‘Auto Run’;
} else {
stepDay();
}
}, 700);
}
});
document.getElementById(‘doseSlider’).addEventListener(‘input’, (e) => {
const val = parseInt(e.target.value);
const label = val < 30 ? 'Low' : val < 70 ? 'Moderate' : 'High / Overwhelming';
document.getElementById('doseVal').innerText = label;
});
init();