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)