| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Lotka-Volterra Predator-Prey Simulation</title> |
| <style> |
| body { |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| max-width: 1000px; |
| margin: 0 auto; |
| padding: 20px; |
| background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); |
| color: #333; |
| } |
| h1 { |
| text-align: center; |
| color: #2c3e50; |
| margin-bottom: 10px; |
| text-shadow: 0 2px 4px rgba(0,0,0,0.1); |
| } |
| h2 { |
| color: #3498db; |
| margin-top: 30px; |
| } |
| .container { |
| background-color: white; |
| padding: 25px; |
| border-radius: 12px; |
| box-shadow: 0 8px 30px rgba(0,0,0,0.15); |
| margin-bottom: 20px; |
| } |
| .description { |
| background-color: #e3f2fd; |
| padding: 15px; |
| border-radius: 8px; |
| margin-bottom: 20px; |
| border-left: 4px solid #3498db; |
| } |
| canvas { |
| border: 1px solid #ddd; |
| margin-top: 20px; |
| background-color: #f9f9f9; |
| border-radius: 8px; |
| } |
| .controls { |
| display: flex; |
| flex-wrap: wrap; |
| gap: 20px; |
| margin-top: 25px; |
| } |
| .control-group { |
| flex: 1; |
| min-width: 200px; |
| background-color: #f8f9fa; |
| padding: 15px; |
| border-radius: 8px; |
| box-shadow: 0 2px 5px rgba(0,0,0,0.05); |
| } |
| .control-group h3 { |
| margin-top: 0; |
| color: #2c3e50; |
| border-bottom: 1px solid #eee; |
| padding-bottom: 8px; |
| } |
| label { |
| display: block; |
| margin-bottom: 8px; |
| font-weight: 600; |
| color: #2c3e50; |
| } |
| input[type="range"] { |
| width: 100%; |
| margin-bottom: 5px; |
| } |
| .value-display { |
| text-align: right; |
| font-size: 14px; |
| color: #7f8c8d; |
| font-weight: bold; |
| } |
| .initial-controls { |
| display: flex; |
| gap: 15px; |
| margin-top: 10px; |
| } |
| .initial-controls input { |
| flex: 1; |
| padding: 8px; |
| border-radius: 4px; |
| border: 1px solid #ddd; |
| } |
| button { |
| background-color: #3498db; |
| color: white; |
| border: none; |
| padding: 12px 20px; |
| border-radius: 6px; |
| cursor: pointer; |
| font-weight: bold; |
| margin-top: 15px; |
| width: 100%; |
| font-size: 16px; |
| transition: all 0.3s ease; |
| box-shadow: 0 2px 5px rgba(0,0,0,0.1); |
| } |
| button:hover { |
| background-color: #2980b9; |
| transform: translateY(-2px); |
| box-shadow: 0 4px 8px rgba(0,0,0,0.15); |
| } |
| .legend { |
| display: flex; |
| justify-content: center; |
| gap: 20px; |
| margin-top: 15px; |
| } |
| .legend-item { |
| display: flex; |
| align-items: center; |
| gap: 5px; |
| } |
| .legend-color { |
| width: 15px; |
| height: 15px; |
| border-radius: 3px; |
| } |
| .help-section { |
| background-color: #fff8e1; |
| padding: 20px; |
| border-radius: 8px; |
| margin-top: 25px; |
| border-left: 4px solid #f39c12; |
| } |
| .help-section h3 { |
| margin-top: 0; |
| color: #e67e22; |
| } |
| .variable { |
| display: flex; |
| margin-bottom: 10px; |
| padding: 8px; |
| background-color: #fff; |
| border-radius: 4px; |
| box-shadow: 0 1px 3px rgba(0,0,0,0.05); |
| } |
| .variable-name { |
| font-weight: bold; |
| color: #2c3e50; |
| min-width: 140px; |
| } |
| .variable-desc { |
| flex: 1; |
| } |
| .equation { |
| background-color: #f8f9fa; |
| padding: 10px; |
| border-radius: 6px; |
| margin: 15px 0; |
| font-family: monospace; |
| text-align: center; |
| border-left: 3px solid #3498db; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>Lotka-Volterra Predator-Prey Simulation</h1> |
| |
| <div class="description"> |
| <p>This simulation demonstrates the classic Lotka-Volterra model of predator-prey dynamics. The populations of prey and predators oscillate in a cyclical pattern based on the mathematical relationships between their growth rates, predation, and death rates.</p> |
| </div> |
| |
| <canvas id="simulationCanvas" width="800" height="400"></canvas> |
| |
| <div class="legend"> |
| <div class="legend-item"> |
| <div class="legend-color" style="background-color: #3498db;"></div> |
| <span>Prey Population</span> |
| </div> |
| <div class="legend-item"> |
| <div class="legend-color" style="background-color: #e74c3c;"></div> |
| <span>Predator Population</span> |
| </div> |
| </div> |
| |
| <div class="controls"> |
| <div class="control-group"> |
| <h3>Population Parameters</h3> |
| |
| <label for="initialPrey">Initial Prey Population: <span id="initialPreyValue">40</span></label> |
| <input type="range" id="initialPrey" min="1" max="200" step="1" value="40"> |
| |
| <label for="initialPredator">Initial Predator Population: <span id="initialPredatorValue">9</span></label> |
| <input type="range" id="initialPredator" min="1" max="50" step="1" value="9"> |
| |
| <div class="initial-controls"> |
| <input type="number" id="preyInput" min="1" max="200" value="40"> |
| <input type="number" id="predatorInput" min="1" max="50" value="9"> |
| </div> |
| </div> |
| |
| <div class="control-group"> |
| <h3>Environmental Parameters</h3> |
| |
| <label for="preyRate">Prey Reproduction Rate: <span id="preyRateValue">0.1</span></label> |
| <input type="range" id="preyRate" min="0.01" max="0.5" step="0.01" value="0.1"> |
| |
| <label for="predationRate">Predation Rate: <span id="predationRateValue">0.01</span></label> |
| <input type="range" id="predationRate" min="0.001" max="0.1" step="0.001" value="0.01"> |
| |
| <label for="predatorDeathRate">Predator Death Rate: <span id="predatorDeathRateValue">0.1</span></label> |
| <input type="range" id="predatorDeathRate" min="0.01" max="0.5" step="0.01" value="0.1"> |
| |
| <label for="predatorEfficiency">Predator Efficiency: <span id="predatorEfficiencyValue">0.01</span></label> |
| <input type="range" id="predatorEfficiency" min="0.001" max="0.1" step="0.001" value="0.01"> |
| </div> |
| </div> |
| |
| <button id="resetBtn">Reset Simulation</button> |
| </div> |
|
|
| <div class="help-section"> |
| <h3>Lotka-Volterra Model Variables</h3> |
| |
| <div class="variable"> |
| <div class="variable-name">X (Prey)</div> |
| <div class="variable-desc">Population of prey species. Increases when not being eaten and decreases due to predation.</div> |
| </div> |
| |
| <div class="variable"> |
| <div class="variable-name">Y (Predator)</div> |
| <div class="variable-desc">Population of predator species. Increases when consuming prey and decreases due to natural death.</div> |
| </div> |
| |
| <div class="variable"> |
| <div class="variable-name">α</div> |
| <div class="variable-desc">Prey reproduction rate. Determines how quickly prey population grows in the absence of predators.</div> |
| </div> |
| |
| <div class="variable"> |
| <div class="variable-name">β</div> |
| <div class="variable-desc">Predation rate. Determines how effectively predators consume prey.</div> |
| </div> |
| |
| <div class="variable"> |
| <div class="variable-name">γ</div> |
| <div class="variable-desc">Predator death rate. Determines how quickly predators die in the absence of prey.</div> |
| </div> |
| |
| <div class="variable"> |
| <div class="variable-name">δ</div> |
| <div class="variable-desc">Predator efficiency. Determines how effectively predators convert consumed prey into predator offspring.</div> |
| </div> |
| |
| <h3>Mathematical Equations</h3> |
| <div class="equation">dX/dt = αX - βXY</div> |
| <div class="equation">dY/dt = δXY - γY</div> |
| |
| <h3>How to Use This Simulation</h3> |
| <p>This simulation shows the oscillating relationship between prey and predator populations. Adjust parameters to see how they affect population dynamics:</p> |
| <ul> |
| <li>Higher prey reproduction rate (α) leads to faster prey population growth</li> |
| <li>Higher predation rate (β) causes more rapid decline in prey population</li> |
| <li>Higher predator death rate (γ) causes faster predator decline when prey is scarce</li> |
| <li>Higher predator efficiency (δ) allows predators to grow faster when prey is abundant</li> |
| </ul> |
| </div> |
|
|
| <script> |
| |
| const canvas = document.getElementById('simulationCanvas'); |
| const ctx = canvas.getContext('2d'); |
| |
| |
| let params = { |
| preyRate: 0.1, |
| predationRate: 0.01, |
| predatorDeathRate: 0.1, |
| predatorEfficiency: 0.01 |
| }; |
| |
| |
| let preyPopulation = 40; |
| let predatorPopulation = 9; |
| |
| |
| const dt = 0.01; |
| let time = 0; |
| const maxTime = 50; |
| |
| |
| let preyHistory = []; |
| let predatorHistory = []; |
| let timeHistory = []; |
| |
| |
| function updateValueDisplays() { |
| document.getElementById('preyRateValue').textContent = params.preyRate.toFixed(2); |
| document.getElementById('predationRateValue').textContent = params.predationRate.toFixed(3); |
| document.getElementById('predatorDeathRateValue').textContent = params.predatorDeathRate.toFixed(2); |
| document.getElementById('predatorEfficiencyValue').textContent = params.predatorEfficiency.toFixed(3); |
| document.getElementById('initialPreyValue').textContent = preyPopulation; |
| document.getElementById('initialPredatorValue').textContent = predatorPopulation; |
| } |
| |
| |
| function updateParams() { |
| params.preyRate = parseFloat(document.getElementById('preyRate').value); |
| params.predationRate = parseFloat(document.getElementById('predationRate').value); |
| params.predatorDeathRate = parseFloat(document.getElementById('predatorDeathRate').value); |
| params.predatorEfficiency = parseFloat(document.getElementById('predatorEfficiency').value); |
| |
| updateValueDisplays(); |
| } |
| |
| |
| function updateInitialPopulations() { |
| const preyInput = document.getElementById('preyInput'); |
| const predatorInput = document.getElementById('predatorInput'); |
| |
| if (preyInput.value >= 1 && preyInput.value <= 200) { |
| preyPopulation = parseInt(preyInput.value); |
| } |
| |
| if (predatorInput.value >= 1 && predatorInput.value <= 50) { |
| predatorPopulation = parseInt(predatorInput.value); |
| } |
| |
| updateValueDisplays(); |
| } |
| |
| |
| function calculatePopulations() { |
| const prey = preyPopulation; |
| const predator = predatorPopulation; |
| |
| |
| const dPrey = params.preyRate * prey - params.predationRate * prey * predator; |
| const dPredator = params.predatorEfficiency * params.predationRate * prey * predator - params.predatorDeathRate * predator; |
| |
| |
| preyPopulation += dPrey * dt; |
| predatorPopulation += dPredator * dt; |
| |
| |
| preyPopulation = Math.max(0, preyPopulation); |
| predatorPopulation = Math.max(0, predatorPopulation); |
| |
| |
| timeHistory.push(time); |
| preyHistory.push(preyPopulation); |
| predatorHistory.push(predatorPopulation); |
| |
| time += dt; |
| } |
| |
| |
| function drawSimulation() { |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| |
| |
| ctx.beginPath(); |
| ctx.strokeStyle = '#95a5a6'; |
| ctx.lineWidth = 1; |
| |
| |
| ctx.moveTo(50, canvas.height - 50); |
| ctx.lineTo(canvas.width - 50, canvas.height - 50); |
| |
| |
| ctx.moveTo(50, 50); |
| ctx.lineTo(50, canvas.height - 50); |
| |
| ctx.stroke(); |
| |
| |
| ctx.fillStyle = '#2c3e50'; |
| ctx.font = '14px Arial'; |
| ctx.fillText('Time', canvas.width/2, canvas.height - 20); |
| ctx.save(); |
| ctx.translate(15, canvas.height/2); |
| ctx.rotate(-Math.PI/2); |
| ctx.fillText('Population', 0, 0); |
| ctx.restore(); |
| |
| |
| ctx.strokeStyle = '#ecf0f1'; |
| ctx.lineWidth = 0.5; |
| |
| |
| for (let i = 1; i < 10; i++) { |
| const x = 50 + (i * (canvas.width - 100) / 10); |
| ctx.beginPath(); |
| ctx.moveTo(x, 50); |
| ctx.lineTo(x, canvas.height - 50); |
| ctx.stroke(); |
| } |
| |
| |
| for (let i = 1; i < 5; i++) { |
| const y = 50 + (i * (canvas.height - 100) / 5); |
| ctx.beginPath(); |
| ctx.moveTo(50, y); |
| ctx.lineTo(canvas.width - 50, y); |
| ctx.stroke(); |
| } |
| |
| |
| const maxPrey = Math.max(...preyHistory, preyPopulation) * 1.2; |
| const maxPredator = Math.max(...predatorHistory, predatorPopulation) * 1.2; |
| |
| |
| ctx.beginPath(); |
| ctx.strokeStyle = '#3498db'; |
| ctx.lineWidth = 2; |
| |
| |
| for (let i = 0; i < preyHistory.length; i++) { |
| const x = 50 + (timeHistory[i] / maxTime) * (canvas.width - 100); |
| const y = canvas.height - 50 - (preyHistory[i] / maxPrey) * (canvas.height - 100); |
| if (i === 0) { |
| ctx.moveTo(x, y); |
| } else { |
| ctx.lineTo(x, y); |
| } |
| } |
| |
| |
| ctx.strokeStyle = '#e74c3c'; |
| for (let i = 0; i < predatorHistory.length; i++) { |
| const x = 50 + (timeHistory[i] / maxTime) * (canvas.width - 100); |
| const y = canvas.height - 50 - (predatorHistory[i] / maxPredator) * (canvas.height - 100); |
| if (i === 0) { |
| ctx.moveTo(x, y); |
| } else { |
| ctx.lineTo(x, y); |
| } |
| } |
| |
| ctx.stroke(); |
| |
| |
| const preyX = 50 + (time / maxTime) * (canvas.width - 100); |
| const preyY = canvas.height - 50 - (preyPopulation / maxPrey) * (canvas.height - 100); |
| const predatorX = 50 + (time / maxTime) * (canvas.width - 100); |
| const predatorY = canvas.height - 50 - (predatorPopulation / maxPredator) * (canvas.height - 100); |
| |
| |
| ctx.fillStyle = '#3498db'; |
| ctx.beginPath(); |
| ctx.arc(preyX, preyY, 5, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| ctx.fillStyle = '#e74c3c'; |
| ctx.beginPath(); |
| ctx.arc(predatorX, predatorY, 5, 0, Math.PI * 2); |
| ctx.fill(); |
| |
| |
| ctx.fillStyle = '#3498db'; |
| ctx.font = '12px Arial'; |
| ctx.fillText(`Prey: ${preyPopulation.toFixed(0)}`, preyX + 10, preyY - 10); |
| |
| ctx.fillStyle = '#e74c3c'; |
| ctx.fillText(`Predator: ${predatorPopulation.toFixed(0)}`, predatorX + 10, predatorY - 10); |
| } |
| |
| |
| function resetSimulation() { |
| time = 0; |
| preyHistory = []; |
| predatorHistory = []; |
| timeHistory = []; |
| } |
| |
| |
| function animate() { |
| calculatePopulations(); |
| drawSimulation(); |
| |
| if (time < maxTime) { |
| requestAnimationFrame(animate); |
| } else { |
| |
| resetSimulation(); |
| animate(); |
| } |
| } |
| |
| |
| document.getElementById('preyRate').addEventListener('input', updateParams); |
| document.getElementById('predationRate').addEventListener('input', updateParams); |
| document.getElementById('predatorDeathRate').addEventListener('input', updateParams); |
| document.getElementById('predatorEfficiency').addEventListener('input', updateParams); |
| |
| |
| document.getElementById('initialPrey').addEventListener('input', function() { |
| const value = parseInt(this.value); |
| document.getElementById('preyInput').value = value; |
| preyPopulation = value; |
| updateValueDisplays(); |
| }); |
| |
| document.getElementById('initialPredator').addEventListener('input', function() { |
| const value = parseInt(this.value); |
| document.getElementById('predatorInput').value = value; |
| predatorPopulation = value; |
| updateValueDisplays(); |
| }); |
| |
| document.getElementById('preyInput').addEventListener('change', updateInitialPopulations); |
| document.getElementById('predatorInput').addEventListener('change', updateInitialPopulations); |
| |
| document.getElementById('resetBtn').addEventListener('click', function() { |
| resetSimulation(); |
| animate(); |
| }); |
| |
| |
| updateValueDisplays(); |
| |
| |
| animate(); |
| </script> |
| </body> |
| </html> |
|
|