Meta Description" name="description" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Thermal Power Plant Simulator</title>
<style>
* { box-sizing: border-box; font-family: 'Segoe UI', Arial, sans-serif; }
body {
background-color: #0f172a;
color: #f8fafc;
margin: 0;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
h1 { margin-bottom: 5px; color: #38bdf8; text-align: center; }
p.subtitle { margin-top: 0; color: #94a3b8; font-size: 0.95rem; text-align: center; }
.container {
width: 100%;
max-width: 900px;
background: #1e293b;
padding: 20px;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0,0,0,0.5);
border: 1px solid #334155;
}
/* Canvas Screen */
canvas {
width: 100%;
height: 380px;
background: #090d16;
border-radius: 8px;
border: 1px solid #334155;
display: block;
}
/* Control Deck */
.controls {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 20px;
background: #0f172a;
padding: 15px;
border-radius: 8px;
}
.control-item {
display: flex;
flex-direction: column;
justify-content: center;
}
label {
font-weight: bold;
color: #38bdf8;
margin-bottom: 8px;
display: flex;
justify-content: space-between;
}
input[type="range"] {
width: 100%;
accent-color: #f97316;
cursor: pointer;
}
/* Telemetry Cards */
.stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
margin-top: 20px;
}
.card {
background: #0f172a;
padding: 12px;
border-radius: 8px;
text-align: center;
border-bottom: 3px solid #38bdf8;
}
.card-label { font-size: 0.8rem; color: #94a3b8; text-transform: uppercase; }
.card-value { font-size: 1.5rem; font-weight: bold; color: #fff; margin-top: 5px; }
/* Explanation Banner */
.explainer {
margin-top: 15px;
padding: 12px;
background: #1e293b;
border-left: 4px solid #f97316;
border-radius: 4px;
font-size: 0.9rem;
color: #cbd5e1;
}
</style>
</head>
<body>
<h1>How a Thermal Power Plant Works</h1>
<p class="subtitle">Drag the throttle below to burn coal and generate power!</p>
<div class="container">
<!-- Visual Simulation Canvas -->
<canvas id="simCanvas" width="860" height="380"></canvas>
<!-- Controls -->
<div class="controls">
<div class="control-item">
<label>Coal Burn Rate (Heat Output): <span id="fireText" style="color:#f97316;">50%</span></label>
<input type="range" id="fireSlider" min="0" max="100" value="50">
</div>
<div class="control-item">
<label>Cooling Fan Speed (Condenser): <span id="coolText" style="color:#38bdf8;">50%</span></label>
<input type="range" id="coolSlider" min="10" max="100" value="50">
</div>
</div>
<!-- Live Metrics -->
<div class="stats">
<div class="card" style="border-color: #f97316;">
<div class="card-label">Steam Heat</div>
<div class="card-value"><span id="tempVal">270</span> Β°C</div>
</div>
<div class="card" style="border-color: #eab308;">
<div class="card-value"><span id="rpmVal">1500</span> RPM</div>
<div class="card-label">Turbine Speed</div>
</div>
<div class="card" style="border-color: #22c55e;">
<div class="card-label">Electricity Output</div>
<div class="card-value"><span id="powerVal">105</span> MW</div>
</div>
</div>
<!-- Step Explainer -->
<div class="explainer" id="statusText">
π₯ <strong>Phase 1:</strong> Coal burns in the furnace to heat water inside the boiler pipes.
</div>
</div>
<script>
const canvas = document.getElementById('simCanvas');
const ctx = canvas.getContext('2d');
// Controls & Telemetry Elements
const fireSlider = document.getElementById('fireSlider');
const coolSlider = document.getElementById('coolSlider');
const fireText = document.getElementById('fireText');
const coolText = document.getElementById('coolText');
const tempVal = document.getElementById('tempVal');
const rpmVal = document.getElementById('rpmVal');
const powerVal = document.getElementById('powerVal');
const statusText = document.getElementById('statusText');
// Physics / Animation state
let heat = 50;
let cooling = 50;
let turbineAngle = 0;
let particleOffset = 0;
fireSlider.addEventListener('input', (e) => {
heat = parseInt(e.target.value);
fireText.innerText = heat + '%';
});
coolSlider.addEventListener('input', (e) => {
cooling = parseInt(e.target.value);
coolText.innerText = cooling + '%';
});
function draw() {
// Clear screen
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Calculated physics
let temp = Math.round(20 + (heat * 5.2));
let rpm = Math.round((heat / 100) * 3000);
let power = Math.round((rpm / 3000) * 210);
tempVal.innerText = temp;
rpmVal.innerText = rpm;
powerVal.innerText = power;
// Dynamic Explainer Text
if (heat === 0) {
statusText.innerHTML = "π€ <strong>Plant Shutdown:</strong> Fire is out. No steam is being generated.";
} else if (heat < 30) {
statusText.innerHTML = "π₯ <strong>Low Power:</strong> Water is warming up. Low steam flow to turbine.";
} else if (heat < 80) {
statusText.innerHTML = "β‘ <strong>Normal Operation:</strong> High pressure steam is spinning the turbine to create power!";
} else {
statusText.innerHTML = "π <strong>Maximum Output:</strong> Boiler running at peak capacity producing 210 MW!";
}
// --- DRAW COMPONENTS ---
// 1. BOILER / FURNACE (Left)
ctx.fillStyle = "#334155";
ctx.fillRect(50, 120, 130, 180);
// Fire Graphic inside Boiler
if (heat > 0) {
let fireHeight = (heat / 100) * 60;
ctx.fillStyle = heat > 70 ? "#ef4444" : "#f97316";
ctx.beginPath();
ctx.arc(115, 270, fireHeight / 2 + 10, 0, Math.PI * 2);
ctx.fill();
}
ctx.fillStyle = "#fff";
ctx.font = "bold 14px sans-serif";
ctx.fillText("1. BOILER", 80, 150);
// 2. STEAM PIPE (Top Bridge)
ctx.strokeStyle = heat > 0 ? "#ef4444" : "#475569";
ctx.lineWidth = 10;
ctx.beginPath();
ctx.moveTo(115, 120);
ctx.lineTo(115, 80);
ctx.lineTo(380, 80);
ctx.lineTo(380, 160);
ctx.stroke();
// 3. TURBINE (Middle)
ctx.fillStyle = "#475569";
ctx.fillRect(340, 160, 80, 80);
// Spinning Blades inside Turbine
turbineAngle += (rpm / 3000) * 0.2;
ctx.save();
ctx.translate(380, 200);
ctx.rotate(turbineAngle);
ctx.strokeStyle = "#38bdf8";
ctx.lineWidth = 4;
for (let i = 0; i < 4; i++) {
ctx.rotate(Math.PI / 2);
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(0, 25);
ctx.stroke();
}
ctx.restore();
ctx.fillStyle = "#fff";
ctx.fillText("2. TURBINE", 345, 145);
// 4. SHAFT to GENERATOR
ctx.strokeStyle = "#94a3b8";
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(420, 200);
ctx.lineTo(520, 200);
ctx.stroke();
// 5. GENERATOR (Right)
ctx.fillStyle = "#0284c7";
ctx.fillRect(520, 160, 90, 80);
ctx.fillStyle = "#fff";
ctx.fillText("3. GENERATOR", 522, 145);
// Electricity Sparks / Wire (To Grid)
ctx.strokeStyle = power > 0 ? "#eab308" : "#475569";
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(610, 200);
ctx.lineTo(780, 200);
ctx.stroke();
// Transmission Tower
ctx.fillStyle = power > 0 ? "#facc15" : "#64748b";
ctx.font = "bold 28px sans-serif";
ctx.fillText("β‘ GRID", 700, 190);
// 6. CONDENSER & WATER RETURN (Bottom Loop)
ctx.fillStyle = "#1e3a8a";
ctx.fillRect(340, 270, 80, 60);
ctx.fillStyle = "#fff";
ctx.font = "11px sans-serif";
ctx.fillText("4. CONDENSER", 342, 305);
// Water Pipe returning to Boiler
ctx.strokeStyle = "#38bdf8";
ctx.lineWidth = 6;
ctx.beginPath();
ctx.moveTo(340, 300);
ctx.lineTo(115, 300);
ctx.lineTo(115, 270);
ctx.stroke();
// Moving Steam/Water Particles
if (heat > 0) {
particleOffset = (particleOffset + (rpm / 500) + 1) % 30;
ctx.fillStyle = "#fbbf24";
// Draw particle along steam pipe
let pX = 115 + particleOffset * 8;
if (pX < 380) {
ctx.beginPath();
ctx.arc(pX, 80, 4, 0, Math.PI * 2);
ctx.fill();
}
}
requestAnimationFrame(draw);
}
draw();
</script>
</body>
</html>
1
1
10KB
10KB
144.0ms
196.0ms
144.0ms