Interactive simulation of oscillatory motion with VB6 and VB.NET code examples
Simple harmonic motion (SHM) is a type of periodic motion where the restoring force is directly proportional to the displacement and acts in the direction opposite to that of displacement. It can serve as a mathematical model for a variety of motions, such as the oscillation of a spring.
The motion is characterized by its:
The equation for simple harmonic motion is:
x = A × cos(2πft + φ)
' VB6 Simple Harmonic Motion Simulation Dim t As Integer Dim amplitude As Double Dim frequency As Double Dim phase As Double Private Sub Form_Load() amplitude = 100 frequency = 0.05 phase = 0 Timer1.Interval = 50 End Sub Private Sub cmdStart_Click() Timer1.Enabled = True End Sub Private Sub cmdPause_Click() Timer1.Enabled = False End Sub Private Sub cmdReset_Click() t = 0 Timer1.Enabled = False DrawPosition End Sub Private Sub Timer1_Timer() t = t + 1 DrawPosition End Sub Private Sub DrawPosition() ' Calculate position using SHM equation Dim x As Double x = amplitude * Cos(2 * 3.14159 * frequency * t + phase) ' Draw the oscillator at position x Shape1.Left = (Me.ScaleWidth / 2) + x - (Shape1.Width / 2) ' Optional: Draw the motion path If chkShowPath.Value = 1 Then DrawPath End If End Sub Private Sub DrawPath() ' Code to draw the oscillation path ' This would typically use Line or Circle methods ' on a PictureBox control End Sub
' VB.NET Simple Harmonic Motion Simulation Public Class SHMForm Private t As Integer = 0 Private amplitude As Double = 100 Private frequency As Double = 0.05 Private phase As Double = 0 Private isRunning As Boolean = False Private points As New List(Of PointF) Private Sub StartButton_Click(sender As Object, e As EventArgs) Handles StartButton.Click isRunning = True Timer1.Start() End Sub Private Sub PauseButton_Click(sender As Object, e As EventArgs) Handles PauseButton.Click isRunning = False Timer1.Stop() End Sub Private Sub ResetButton_Click(sender As Object, e As EventArgs) Handles ResetButton.Click t = 0 isRunning = False Timer1.Stop() points.Clear() PictureBox1.Invalidate() End Sub Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick t += 1 DrawOscillator() End Sub Private Sub DrawOscillator() ' Calculate position using SHM equation Dim x As Double = amplitude * Math.Cos(2 * Math.PI * frequency * t + phase) ' Position the oscillator Dim centerX As Integer = PictureBox1.Width / 2 Dim centerY As Integer = PictureBox1.Height / 2 Dim ballX As Integer = centerX + CInt(x) - (Ball.Width / 2) Ball.Left = ballX ' Store point for path drawing If chkShowPath.Checked Then points.Add(New PointF(ballX + (Ball.Width / 2), centerY)) If points.Count > 200 Then points.RemoveAt(0) End If ' Redraw the PictureBox PictureBox1.Invalidate() End Sub Private Sub PictureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PictureBox1.Paint ' Draw equilibrium line Dim centerY As Integer = PictureBox1.Height / 2 e.Graphics.DrawLine(Pens.Gray, 0, centerY, PictureBox1.Width, centerY) ' Draw motion path if enabled If chkShowPath.Checked AndAlso points.Count > 1 Then e.Graphics.DrawCurve(Pens.Blue, points.ToArray()) End If End Sub End Class
// JavaScript SHM Simulation const canvas = document.getElementById('simulation-canvas'); const ctx = canvas.getContext('2d'); const graphCanvas = document.getElementById('graph-canvas'); const graphCtx = graphCanvas.getContext('2d'); // Initialize canvas dimensions function initCanvas() { canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; graphCanvas.width = graphCanvas.offsetWidth; graphCanvas.height = graphCanvas.offsetHeight; } // Simulation variables let amplitude = 100; let frequency = 0.05; let phase = 0; let damping = 0; let time = 0; let isRunning = false; let animationId; const positions = []; // Get control elements const amplitudeSlider = document.getElementById('amplitude'); const frequencySlider = document.getElementById('frequency'); const phaseSlider = document.getElementById('phase'); const dampingSlider = document.getElementById('damping'); const startBtn = document.getElementById('start-btn'); const pauseBtn = document.getElementById('pause-btn'); const resetBtn = document.getElementById('reset-btn'); // Update value displays amplitudeSlider.addEventListener('input', () => { amplitude = parseInt(amplitudeSlider.value); document.getElementById('amplitude-value').textContent = amplitude; }); frequencySlider.addEventListener('input', () => { frequency = parseInt(frequencySlider.value) / 100; document.getElementById('frequency-value').textContent = frequencySlider.value; }); phaseSlider.addEventListener('input', () => { phase = parseInt(phaseSlider.value) / 100; document.getElementById('phase-value').textContent = phase.toFixed(2); }); dampingSlider.addEventListener('input', () => { damping = parseInt(dampingSlider.value) / 1000; document.getElementById('damping-value').textContent = dampingSlider.value; }); // Button event handlers startBtn.addEventListener('click', () => { if (!isRunning) { isRunning = true; animate(); } }); pauseBtn.addEventListener('click', () => { isRunning = false; if (animationId) { cancelAnimationFrame(animationId); } }); resetBtn.addEventListener('click', () => { isRunning = false; time = 0; positions.length = 0; if (animationId) { cancelAnimationFrame(animationId); } draw(); }); // Initialize canvas initCanvas(); window.addEventListener('resize', initCanvas); // Main animation function function animate() { if (!isRunning) return; time += 0.05; draw(); animationId = requestAnimationFrame(animate); } // Draw the simulation function draw() { const width = canvas.width; const height = canvas.height; const centerX = width / 2; const centerY = height / 2; // Clear canvas ctx.clearRect(0, 0, width, height); // Draw equilibrium line ctx.beginPath(); ctx.moveTo(0, centerY); ctx.lineTo(width, centerY); ctx.strokeStyle = '#aaa'; ctx.stroke(); // Calculate position with damping const effectiveAmplitude = amplitude * Math.exp(-damping * time); const x = effectiveAmplitude * Math.cos(2 * Math.PI * frequency * time + phase); const ballX = centerX + x; const ballY = centerY; // Draw spring drawSpring(centerX, centerY, ballX, ballY); // Draw oscillator (ball) ctx.beginPath(); ctx.arc(ballX, ballY, 20, 0, Math.PI * 2); ctx.fillStyle = '#5c6bc0'; ctx.fill(); ctx.strokeStyle = '#1a237e'; ctx.lineWidth = 2; ctx.stroke(); // Draw graph drawGraph(); } // Draw spring between two points function drawSpring(x1, y1, x2, y2) { const dx = x2 - x1; const dy = y2 - y1; const distance = Math.sqrt(dx * dx + dy * dy); const segments = 16; const segmentLength = distance / segments; ctx.beginPath(); ctx.moveTo(x1, y1); for (let i = 1; i <= segments; i++) { const x = x1 + (dx * i) / segments; let y; if (i % 2 === 0) { // Bottom of coil y = y1 + (dy * i) / segments - 10; } else { // Top of coil y = y1 + (dy * i) / segments + 10; } ctx.lineTo(x, y); } ctx.strokeStyle = '#888'; ctx.lineWidth = 2; ctx.stroke(); } // Draw position graph function drawGraph() { const width = graphCanvas.width; const height = graphCanvas.height; const centerY = height / 2; // Clear graph graphCtx.clearRect(0, 0, width, height); // Draw time axis graphCtx.beginPath(); graphCtx.moveTo(0, centerY); graphCtx.lineTo(width, centerY); graphCtx.strokeStyle = '#aaa'; graphCtx.stroke(); // Add current position to history const effectiveAmplitude = amplitude * Math.exp(-damping * time); const x = effectiveAmplitude * Math.cos(2 * Math.PI * frequency * time + phase); positions.push({time, x}); // Keep only the most recent points if (positions.length > 200) { positions.shift(); } // Draw graph line if (positions.length > 1) { graphCtx.beginPath(); const startIndex = Math.max(0, positions.length - width / 2); for (let i = startIndex; i < positions.length; i++) { const point = positions[i]; const graphX = width - (positions.length - i) * 2; const graphY = centerY - point.x * 0.5; if (i === startIndex) { graphCtx.moveTo(graphX, graphY); } else { graphCtx.lineTo(graphX, graphY); } } graphCtx.strokeStyle = '#e91e63'; graphCtx.lineWidth = 2; graphCtx.stroke(); } } // Initial draw draw();
Modify the simulation to include:
Observe how changing these parameters affects the motion according to the equations:
ω = √(k/m)
T = 2π√(m/k)