Created
April 9, 2025 08:24
-
-
Save ritwikraha/222e5a0f55920843db10cfa35780935d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Unit Circle and Sine Wave Animation</title> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
margin: 20px; | |
} | |
.container { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
} | |
.animation-container { | |
display: flex; | |
align-items: center; | |
margin-bottom: 20px; | |
} | |
canvas { | |
border: 1px solid #ccc; | |
} | |
.controls { | |
margin: 20px 0; | |
text-align: center; | |
} | |
.info { | |
margin: 20px 0; | |
width: 600px; | |
text-align: center; | |
} | |
label { | |
margin-right: 10px; | |
} | |
input[type="range"] { | |
vertical-align: middle; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h1>Unit Circle and Sine Wave Animation</h1> | |
<div class="animation-container"> | |
<canvas id="circleCanvas" width="300" height="300"></canvas> | |
<canvas id="waveCanvas" width="600" height="300"></canvas> | |
</div> | |
<div class="controls"> | |
<label for="frequencySlider">Angular Frequency (ω = 2πf):</label> | |
<input type="range" id="frequencySlider" min="0.5" max="5" step="0.1" value="1"> | |
<span id="frequencyValue">1.0</span> | |
<button id="pauseButton">Pause</button> | |
<button id="resetButton">Reset</button> | |
</div> | |
<div class="info"> | |
<p>This animation demonstrates the relationship between a point moving on a unit circle and a sine wave. The x-coordinate of the point on the circle (cos(θ)) and the y-coordinate (sin(θ)) are plotted over time.</p> | |
<p>Angle θ = ωt, where ω is the angular frequency (2πf) and t is time.</p> | |
</div> | |
</div> | |
<script> | |
// Get canvas elements and contexts | |
const circleCanvas = document.getElementById('circleCanvas'); | |
const waveCanvas = document.getElementById('waveCanvas'); | |
const circleCtx = circleCanvas.getContext('2d'); | |
const waveCtx = waveCanvas.getContext('2d'); | |
// Get UI elements | |
const frequencySlider = document.getElementById('frequencySlider'); | |
const frequencyValue = document.getElementById('frequencyValue'); | |
const pauseButton = document.getElementById('pauseButton'); | |
const resetButton = document.getElementById('resetButton'); | |
// Constants and variables | |
const circleRadius = 100; | |
const circleCenterX = circleCanvas.width / 2; | |
const circleCenterY = circleCanvas.height / 2; | |
const waveStartX = 0; | |
const waveHeight = 200; | |
const waveCenterY = waveCanvas.height / 2; | |
let time = 0; | |
let frequency = parseFloat(frequencySlider.value); | |
let wavePoints = []; | |
let animating = true; | |
let animationId; | |
// Update frequency display | |
function updateFrequencyDisplay() { | |
frequency = parseFloat(frequencySlider.value); | |
frequencyValue.textContent = frequency.toFixed(1); | |
} | |
// Initialize the wave points array | |
function initWavePoints() { | |
wavePoints = []; | |
for (let i = 0; i < waveCanvas.width; i++) { | |
wavePoints.push(0); | |
} | |
} | |
// Draw the unit circle with the current angle | |
function drawCircle(angle) { | |
circleCtx.clearRect(0, 0, circleCanvas.width, circleCanvas.height); | |
// Draw coordinate axes | |
circleCtx.strokeStyle = '#999'; | |
circleCtx.beginPath(); | |
circleCtx.moveTo(0, circleCenterY); | |
circleCtx.lineTo(circleCanvas.width, circleCenterY); | |
circleCtx.moveTo(circleCenterX, 0); | |
circleCtx.lineTo(circleCenterX, circleCanvas.height); | |
circleCtx.stroke(); | |
// Draw the unit circle | |
circleCtx.strokeStyle = '#333'; | |
circleCtx.beginPath(); | |
circleCtx.arc(circleCenterX, circleCenterY, circleRadius, 0, 2 * Math.PI); | |
circleCtx.stroke(); | |
// Calculate the point on the circle | |
const x = Math.cos(angle) * circleRadius; | |
const y = Math.sin(angle) * circleRadius; | |
const pointX = circleCenterX + x; | |
const pointY = circleCenterY - y; // Flip y to match canvas coordinates | |
// Draw the vector from center to point | |
circleCtx.strokeStyle = 'red'; | |
circleCtx.lineWidth = 2; | |
circleCtx.beginPath(); | |
circleCtx.moveTo(circleCenterX, circleCenterY); | |
circleCtx.lineTo(pointX, pointY); | |
circleCtx.stroke(); | |
// Draw the point | |
circleCtx.fillStyle = 'red'; | |
circleCtx.beginPath(); | |
circleCtx.arc(pointX, pointY, 5, 0, 2 * Math.PI); | |
circleCtx.fill(); | |
// Draw the projection on x-axis | |
circleCtx.strokeStyle = 'blue'; | |
circleCtx.setLineDash([5, 3]); | |
circleCtx.beginPath(); | |
circleCtx.moveTo(pointX, pointY); | |
circleCtx.lineTo(pointX, circleCenterY); | |
circleCtx.stroke(); | |
circleCtx.setLineDash([]); | |
// Draw the projection on y-axis | |
circleCtx.strokeStyle = 'green'; | |
circleCtx.setLineDash([5, 3]); | |
circleCtx.beginPath(); | |
circleCtx.moveTo(pointX, pointY); | |
circleCtx.lineTo(circleCenterX, pointY); | |
circleCtx.stroke(); | |
circleCtx.setLineDash([]); | |
// Draw the angle | |
circleCtx.strokeStyle = 'orange'; | |
circleCtx.beginPath(); | |
circleCtx.arc(circleCenterX, circleCenterY, 20, 0, -angle, true); | |
circleCtx.stroke(); | |
// Draw angle label | |
circleCtx.fillStyle = 'black'; | |
circleCtx.font = '14px Arial'; | |
circleCtx.fillText(`θ = ${angle.toFixed(2)} rad`, circleCenterX + 25, circleCenterY - 25); | |
// Draw the projection points | |
circleCtx.fillStyle = 'blue'; | |
circleCtx.beginPath(); | |
circleCtx.arc(pointX, circleCenterY, 5, 0, 2 * Math.PI); | |
circleCtx.fill(); | |
circleCtx.fillStyle = 'green'; | |
circleCtx.beginPath(); | |
circleCtx.arc(circleCenterX, pointY, 5, 0, 2 * Math.PI); | |
circleCtx.fill(); | |
// Draw labels | |
circleCtx.fillStyle = 'blue'; | |
circleCtx.fillText(`cos(θ) = ${Math.cos(angle).toFixed(2)}`, circleCenterX - 90, circleCenterY + 30); | |
circleCtx.fillStyle = 'green'; | |
circleCtx.fillText(`sin(θ) = ${Math.sin(angle).toFixed(2)}`, circleCenterX + 20, pointY - 10); | |
return { x: Math.cos(angle), y: Math.sin(angle) }; | |
} | |
// Update and draw the wave | |
function updateWave(angle, point) { | |
// Shift all points to the left | |
for (let i = 0; i < wavePoints.length - 1; i++) { | |
wavePoints[i] = wavePoints[i + 1]; | |
} | |
// Add the new point | |
wavePoints[wavePoints.length - 1] = point.y; | |
// Clear the wave canvas | |
waveCtx.clearRect(0, 0, waveCanvas.width, waveCanvas.height); | |
// Draw coordinate axes | |
waveCtx.strokeStyle = '#999'; | |
waveCtx.beginPath(); | |
waveCtx.moveTo(0, waveCenterY); | |
waveCtx.lineTo(waveCanvas.width, waveCenterY); | |
waveCtx.stroke(); | |
// Draw time labels on x-axis | |
waveCtx.fillStyle = 'black'; | |
waveCtx.font = '12px Arial'; | |
waveCtx.fillText('0', 5, waveCenterY + 15); | |
waveCtx.fillText('π', waveCanvas.width / 4, waveCenterY + 15); | |
waveCtx.fillText('2π', waveCanvas.width / 2, waveCenterY + 15); | |
waveCtx.fillText('3π', 3 * waveCanvas.width / 4, waveCenterY + 15); | |
waveCtx.fillText('4π', waveCanvas.width - 20, waveCenterY + 15); | |
// Draw the wave | |
waveCtx.strokeStyle = 'green'; | |
waveCtx.lineWidth = 2; | |
waveCtx.beginPath(); | |
for (let i = 0; i < wavePoints.length; i++) { | |
const y = waveCenterY - wavePoints[i] * circleRadius; | |
if (i === 0) { | |
waveCtx.moveTo(i, y); | |
} else { | |
waveCtx.lineTo(i, y); | |
} | |
} | |
waveCtx.stroke(); | |
// Draw vertical line connecting to current point | |
waveCtx.strokeStyle = 'red'; | |
waveCtx.setLineDash([5, 3]); | |
waveCtx.beginPath(); | |
waveCtx.moveTo(0, waveCenterY - point.y * circleRadius); | |
waveCtx.lineTo(0, waveCenterY); | |
waveCtx.stroke(); | |
waveCtx.setLineDash([]); | |
// Draw the current point | |
waveCtx.fillStyle = 'red'; | |
waveCtx.beginPath(); | |
waveCtx.arc(0, waveCenterY - point.y * circleRadius, 5, 0, 2 * Math.PI); | |
waveCtx.fill(); | |
// Draw phase information | |
waveCtx.fillStyle = 'black'; | |
waveCtx.font = '14px Arial'; | |
waveCtx.fillText(`ω = ${frequency.toFixed(1)} rad/s`, waveCanvas.width - 150, 30); | |
waveCtx.fillText(`t = ${time.toFixed(2)} s`, waveCanvas.width - 150, 50); | |
waveCtx.fillText(`θ = ωt = ${angle.toFixed(2)} rad`, waveCanvas.width - 150, 70); | |
} | |
// Animation loop | |
function animate() { | |
if (!animating) return; | |
// Calculate the current angle based on time and frequency | |
// ω = 2πf, θ = ωt | |
const angle = (frequency * time) % (2 * Math.PI); | |
// Draw the circle and get the point coordinates | |
const point = drawCircle(angle); | |
// Update and draw the wave | |
updateWave(angle, point); | |
// Increment time | |
time += 0.02; | |
// Continue the animation loop | |
animationId = requestAnimationFrame(animate); | |
} | |
// Initialize and start animation | |
function init() { | |
initWavePoints(); | |
time = 0; | |
updateFrequencyDisplay(); | |
animate(); | |
} | |
// Event listeners | |
frequencySlider.addEventListener('input', updateFrequencyDisplay); | |
pauseButton.addEventListener('click', function() { | |
animating = !animating; | |
if (animating) { | |
pauseButton.textContent = 'Pause'; | |
animate(); | |
} else { | |
pauseButton.textContent = 'Resume'; | |
cancelAnimationFrame(animationId); | |
} | |
}); | |
resetButton.addEventListener('click', function() { | |
time = 0; | |
initWavePoints(); | |
}); | |
// Start the animation | |
init(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment