bolt
Physics
Topic: Mechanics & Motion
Subtopic: Periodic Motion & Pendulum
Level: grade 9 10
Difficulty: intermediate
⏱ 15 mins
Simple Pendulum & Gravitational Acceleration Lab
Interactive physics laboratory investigating period, velocity, and gravitational acceleration.
Ready / Running
Speed:
sync
Compiling simulation engine...
Theoretical Principle
For small angular displacements ($theta le 15^circ$), the restoring force is proportional to displacement. The period is governed by:
$$T = 2pisqrt{frac{L}{g}}$$
Notice that period $T$ is completely independent of the bob mass $m$, a key revelation discovered by Galileo Galilei!
- Set length to 1.0 m on Earth ($g = 9.8text{ m/s}^2$). Record the period.
- Switch gravity to Moon ($g = 1.6text{ m/s}^2$) and observe the dramatically slower motion.
- Double the mass slider and verify whether period changes.
(function() {
var canvas = document.getElementById('pendulumCanvas');
if (!canvas) return;
var ctx = canvas.getContext('2d');
var originX = canvas.width / 2;
var originY = 50;
var length = 1.0;
var gravity = 9.8;
var mass = 1.0;
var damping = 0.001;
var angle = Math.PI / 7;
var angleV = 0.0;
var angleA = 0.0;
var isRunning = true;
var lenSlider = document.getElementById('lenSlider');
var gSlider = document.getElementById('gSlider');
var massSlider = document.getElementById('massSlider');
var dragSlider = document.getElementById('dragSlider');
var playBtn = document.getElementById('playBtn');
var resetBtn = document.getElementById('resetBtn');
if (lenSlider) {
lenSlider.oninput = function() {
length = parseFloat(this.value);
document.getElementById('lenVal').innerText = length.toFixed(1) + ' m';
};
}
if (gSlider) {
gSlider.oninput = function() {
gravity = parseFloat(this.value);
document.getElementById('gVal').innerText = gravity.toFixed(1) + ' m/s²';
};
}
if (massSlider) {
massSlider.oninput = function() {
mass = parseFloat(this.value);
document.getElementById('massVal').innerText = mass.toFixed(1) + ' kg';
};
}
if (dragSlider) {
dragSlider.oninput = function() {
damping = parseFloat(this.value);
document.getElementById('dragVal').innerText = (damping === 0) ? 'None' : (damping > 0.003 ? 'High' : 'Moderate');
};
}
window.setG = function(val, label) {
gravity = val;
if (gSlider) gSlider.value = val;
document.getElementById('gVal').innerText = label;
};
if (playBtn) {
playBtn.onclick = function() {
isRunning = !isRunning;
playBtn.innerText = isRunning ? '⏸️ Pause' : '▶️ Resume';
};
}
if (resetBtn) {
resetBtn.onclick = function() {
angle = Math.PI / 7;
angleV = 0.0;
angleA = 0.0;
};
}
function updatePhysics() {
if (!isRunning) return;
var dt = 0.16;
angleA = (-1 * (gravity / (length * 10)) * Math.sin(angle)) - (damping * angleV);
angleV += angleA * dt;
angle += angleV * dt;
var period = 2 * Math.PI * Math.sqrt(length / gravity);
document.getElementById('tReadout').innerText = period.toFixed(2) + ' s';
document.getElementById('angReadout').innerText = ((angle * 180) / Math.PI).toFixed(1) + '°';
document.getElementById('vReadout').innerText = (angleV * length * 2.5).toFixed(2) + ' m/s';
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Grid lines / ceiling
ctx.fillStyle = '#1e293b';
ctx.fillRect(0, 0, canvas.width, originY);
ctx.strokeStyle = '#334155';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(0, originY);
ctx.lineTo(canvas.width, originY);
ctx.stroke();
// Origin Pivot
ctx.beginPath();
ctx.arc(originX, originY, 8, 0, 2 * Math.PI);
ctx.fillStyle = '#38bdf8';
ctx.fill();
// Current Bob position
var pixelLen = length * 160;
var bobX = originX + pixelLen * Math.sin(angle);
var bobY = originY + pixelLen * Math.cos(angle);
// Protractor arc
ctx.beginPath();
ctx.setLineDash([3, 4]);
ctx.strokeStyle = 'rgba(148, 163, 184, 0.2)';
ctx.arc(originX, originY, pixelLen, 0, Math.PI);
ctx.stroke();
ctx.setLineDash([]);
// Rod / String
ctx.beginPath();
ctx.moveTo(originX, originY);
ctx.lineTo(bobX, bobY);
ctx.strokeStyle = '#e2e8f0';
ctx.lineWidth = 3;
ctx.stroke();
// Bob Sphere
var bobRadius = 14 + (mass * 6);
var grad = ctx.createRadialGradient(bobX - 4, bobY - 4, 2, bobX, bobY, bobRadius);
grad.addColorStop(0, '#7dd3fc');
grad.addColorStop(0.7, '#0284c7');
grad.addColorStop(1, '#0369a1');
ctx.beginPath();
ctx.arc(bobX, bobY, bobRadius, 0, 2 * Math.PI);
ctx.fillStyle = grad;
ctx.fill();
ctx.strokeStyle = '#bae6fd';
ctx.lineWidth = 2;
ctx.stroke();
}
function loop() {
updatePhysics();
draw();
requestAnimationFrame(loop);
}
loop();
})();
<div class="sim-container">
<div class="sim-canvas-wrap">
<canvas id="pendulumCanvas" width="600" height="420"></canvas>
<div class="sim-readouts">
<div class="readout-box"><span>Period (T):</span> <strong id="tReadout">2.01 s</strong></div>
<div class="readout-box"><span>Angle (θ):</span> <strong id="angReadout">25.0°</strong></div>
<div class="readout-box"><span>Velocity (v):</span> <strong id="vReadout">0.0 m/s</strong></div>
</div>
</div>
<div class="sim-controls-panel">
<div class="control-group">
<label>String Length (L): <span id="lenVal">1.0 m</span></label>
<input type="range" id="lenSlider" min="0.4" max="2.0" step="0.1" value="1.0">
</div>
<div class="control-group">
<label>Gravity (g): <span id="gVal">9.8 m/s² (Earth)</span></label>
<input type="range" id="gSlider" min="1.6" max="25.0" step="0.1" value="9.8">
<div class="quick-preset-btns">
<button type="button" onclick="setG(1.6, 'Moon (1.6 m/s²)')">Moon</button>
<button type="button" onclick="setG(9.8, 'Earth (9.8 m/s²)')">Earth</button>
<button type="button" onclick="setG(24.8, 'Jupiter (24.8 m/s²)')">Jupiter</button>
</div>
</div>
<div class="control-group">
<label>Bob Mass (m): <span id="massVal">1.0 kg</span></label>
<input type="range" id="massSlider" min="0.5" max="3.0" step="0.2" value="1.0">
</div>
<div class="control-group">
<label>Damping / Air Resistance: <span id="dragVal">Low</span></label>
<input type="range" id="dragSlider" min="0" max="0.005" step="0.0005" value="0.001">
</div>
<div class="btn-actions">
<button type="button" id="playBtn" class="primary-btn">⏸️ Pause</button>
<button type="button" id="resetBtn" class="secondary-btn">🔄 Reset</button>
</div>
</div>
</div>
.sim-container { display: flex; flex-direction: column; gap: 16px; background: #0f172a; border-radius: 16px; padding: 16px; color: #fff; font-family: inherit; }
@media (min-width: 768px) { .sim-container { flex-direction: row; } }
.sim-canvas-wrap { flex: 1; position: relative; background: #020617; border-radius: 12px; overflow: hidden; border: 1px solid #1e293b; display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 420px; }
canvas { max-width: 100%; height: auto; display: block; }
.sim-readouts { position: absolute; top: 12px; left: 12px; display: flex; flex-direction: column; gap: 6px; pointer-events: none; }
.readout-box { background: rgba(15, 23, 42, 0.75); backdrop-filter: blur(8px); padding: 5px 10px; border-radius: 6px; font-size: 11px; font-weight: 700; border: 1px solid rgba(255,255,255,0.1); }
.readout-box span { color: #94a3b8; }
.readout-box strong { color: #38bdf8; font-family: monospace; }
.sim-controls-panel { width: 100%; max-width: 280px; background: #1e293b; padding: 16px; border-radius: 12px; border: 1px solid #334155; display: flex; flex-direction: column; gap: 14px; }
.control-group label { display: flex; justify-content: space-between; font-size: 11px; font-weight: 800; color: #cbd5e1; margin-bottom: 6px; text-transform: uppercase; }
.control-group input[type="range"] { width: 100%; accent-color: #38bdf8; cursor: pointer; }
.quick-preset-btns { display: flex; gap: 4px; margin-top: 6px; }
.quick-preset-btns button { flex: 1; font-size: 9px; font-weight: 800; padding: 4px 6px; border-radius: 4px; border: 1px solid #475569; background: #0f172a; color: #94a3b8; cursor: pointer; }
.quick-preset-btns button:hover { background: #38bdf8; color: #020617; border-color: #38bdf8; }
.btn-actions { display: flex; gap: 8px; margin-top: 10px; }
.btn-actions button { flex: 1; padding: 10px; border-radius: 8px; border: none; font-weight: 800; font-size: 12px; cursor: pointer; transition: all 0.2s; }
.primary-btn { background: #38bdf8; color: #020617; }
.primary-btn:hover { background: #7dd3fc; }
.secondary-btn { background: #334155; color: #fff; }
.secondary-btn:hover { background: #475569; }