Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
start [2025/04/15 10:20] – kymki | start [2025/04/15 10:24] (current) – kymki | ||
---|---|---|---|
Line 9: | Line 9: | ||
It’s built with simplicity and functionality in mind.< | It’s built with simplicity and functionality in mind.< | ||
For your own safety, use the Ramble Meter as an indicator of the state of the information in a post.< | For your own safety, use the Ramble Meter as an indicator of the state of the information in a post.< | ||
+ | In the < | ||
</p> | </p> | ||
Line 14: | Line 15: | ||
</ | </ | ||
</ | </ | ||
- | |||
- | |||
- | < | ||
- | < | ||
- | <meta charset=" | ||
- | <meta name=" | ||
- | < | ||
- | < | ||
- | body { | ||
- | font-family: | ||
- | max-width: 900px; | ||
- | margin: 0 auto; | ||
- | padding: 20px; | ||
- | background-color: | ||
- | } | ||
- | .container { | ||
- | background-color: | ||
- | border-radius: | ||
- | padding: 20px; | ||
- | box-shadow: 0 2px 10px rgba(0, | ||
- | } | ||
- | h1 { | ||
- | color: #333; | ||
- | text-align: center; | ||
- | } | ||
- | .controls { | ||
- | display: grid; | ||
- | grid-template-columns: | ||
- | gap: 20px; | ||
- | margin-bottom: | ||
- | } | ||
- | .control-group { | ||
- | background-color: | ||
- | padding: 15px; | ||
- | border-radius: | ||
- | } | ||
- | .control-title { | ||
- | font-weight: | ||
- | margin-bottom: | ||
- | } | ||
- | .slider-container { | ||
- | margin-bottom: | ||
- | } | ||
- | .slider-container label { | ||
- | display: block; | ||
- | margin-bottom: | ||
- | } | ||
- | .slider-container input[type=" | ||
- | width: 100%; | ||
- | } | ||
- | .slider-value { | ||
- | font-weight: | ||
- | margin-left: | ||
- | } | ||
- | canvas { | ||
- | display: block; | ||
- | margin: 0 auto; | ||
- | border: 1px solid #ddd; | ||
- | background-color: | ||
- | } | ||
- | .explanation { | ||
- | margin-top: 20px; | ||
- | background-color: | ||
- | padding: 15px; | ||
- | border-radius: | ||
- | border-left: | ||
- | } | ||
- | .checkbox-container { | ||
- | margin-top: 10px; | ||
- | } | ||
- | button { | ||
- | background-color: | ||
- | color: white; | ||
- | border: none; | ||
- | padding: 8px 16px; | ||
- | border-radius: | ||
- | cursor: pointer; | ||
- | font-size: 14px; | ||
- | margin-right: | ||
- | } | ||
- | button: | ||
- | background-color: | ||
- | } | ||
- | .button-container { | ||
- | text-align: center; | ||
- | margin: 15px 0; | ||
- | } | ||
- | </ | ||
- | </ | ||
- | < | ||
- | <div class=" | ||
- | < | ||
- | | ||
- | <div class=" | ||
- | <div class=" | ||
- | <div class=" | ||
- | <div class=" | ||
- | <label for=" | ||
- | <input type=" | ||
- | </ | ||
- | <div class=" | ||
- | <label for=" | ||
- | <input type=" | ||
- | </ | ||
- | <div class=" | ||
- | <label for=" | ||
- | <input type=" | ||
- | </ | ||
- | </ | ||
- | | ||
- | <div class=" | ||
- | <div class=" | ||
- | <div class=" | ||
- | <label for=" | ||
- | <input type=" | ||
- | </ | ||
- | <div class=" | ||
- | <label for=" | ||
- | <input type=" | ||
- | </ | ||
- | <div class=" | ||
- | <input type=" | ||
- | <label for=" | ||
- | </ | ||
- | </ | ||
- | </ | ||
- | | ||
- | <div class=" | ||
- | <button id=" | ||
- | <button id=" | ||
- | </ | ||
- | | ||
- | <canvas id=" | ||
- | | ||
- | <div class=" | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
- | <ul> | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
- | </ul> | ||
- | </ | ||
- | </ | ||
- | |||
- | < | ||
- | // Canvas and context | ||
- | const canvas = document.getElementById(' | ||
- | const ctx = canvas.getContext(' | ||
- | | ||
- | // Controls | ||
- | const slitCountSlider = document.getElementById(' | ||
- | const slitWidthSlider = document.getElementById(' | ||
- | const slitSeparationSlider = document.getElementById(' | ||
- | const wavelengthSlider = document.getElementById(' | ||
- | const intensitySlider = document.getElementById(' | ||
- | const showIndividualCheckbox = document.getElementById(' | ||
- | const animateBtn = document.getElementById(' | ||
- | const resetBtn = document.getElementById(' | ||
- | | ||
- | // Value displays | ||
- | const slitCountValue = document.getElementById(' | ||
- | const slitWidthValue = document.getElementById(' | ||
- | const slitSeparationValue = document.getElementById(' | ||
- | const wavelengthValue = document.getElementById(' | ||
- | const intensityValue = document.getElementById(' | ||
- | | ||
- | // Animation variables | ||
- | let animationId = null; | ||
- | let isAnimating = false; | ||
- | let time = 0; | ||
- | | ||
- | // Constants | ||
- | const SCREEN_DISTANCE = 1000; // Distance from slits to screen in nm | ||
- | | ||
- | // Initialize the canvas | ||
- | function initCanvas() { | ||
- | ctx.fillStyle = ' | ||
- | ctx.fillRect(0, | ||
- | | ||
- | // Draw slits | ||
- | drawSlits(); | ||
- | | ||
- | // Draw initial pattern | ||
- | drawInterferencePattern(); | ||
- | } | ||
- | | ||
- | // Draw the slits on the left side of the canvas | ||
- | function drawSlits() { | ||
- | const slitCount = parseInt(slitCountSlider.value); | ||
- | const slitWidth = parseInt(slitWidthSlider.value); | ||
- | const slitSeparation = parseInt(slitSeparationSlider.value); | ||
- | | ||
- | // Clear the slit area | ||
- | ctx.fillStyle = ' | ||
- | ctx.fillRect(0, | ||
- | | ||
- | // Draw barrier | ||
- | ctx.fillStyle = '# | ||
- | ctx.fillRect(20, | ||
- | | ||
- | // Calculate total height of all slits and spaces | ||
- | const totalHeight = (slitCount * slitWidth) + ((slitCount - 1) * slitSeparation); | ||
- | const startY = (canvas.height - totalHeight) / 2; | ||
- | | ||
- | // Draw slits | ||
- | ctx.fillStyle = ' | ||
- | for (let i = 0; i < slitCount; i++) { | ||
- | const slitY = startY + i * (slitWidth + slitSeparation); | ||
- | ctx.fillRect(20, | ||
- | } | ||
- | } | ||
- | | ||
- | // Calculate the intensity at a given point on the screen | ||
- | function calculateIntensity(y) { | ||
- | const slitCount = parseInt(slitCountSlider.value); | ||
- | const slitWidth = parseInt(slitWidthSlider.value) / 10; // Convert to suitable units | ||
- | const slitSeparation = parseInt(slitSeparationSlider.value) / 10; // Convert to suitable units | ||
- | const wavelength = parseInt(wavelengthSlider.value) / 100; // Convert to suitable units | ||
- | const intensity = parseInt(intensitySlider.value) / 100; | ||
- | | ||
- | // Calculate center position of the slits | ||
- | const centerPosition = canvas.height / 2; | ||
- | | ||
- | // Calculate the total intensity from all slits | ||
- | let totalIntensity = 0; | ||
- | let individualIntensities = []; | ||
- | | ||
- | // Calculate the center position of the first slit | ||
- | const firstSlitCenter = centerPosition - (slitCount - 1) * (slitWidth + slitSeparation) / 2; | ||
- | | ||
- | for (let i = 0; i < slitCount; i++) { | ||
- | // Calculate the position of the current slit | ||
- | const slitCenter = firstSlitCenter + i * (slitWidth + slitSeparation); | ||
- | | ||
- | // Calculate the path difference | ||
- | const pathDifference = Math.abs(y - slitCenter) * wavelength / SCREEN_DISTANCE; | ||
- | | ||
- | // Calculate the phase difference | ||
- | const phaseDifference = 2 * Math.PI * pathDifference / wavelength; | ||
- | | ||
- | // Calculate the amplitude contribution from this slit | ||
- | const amplitude = Math.cos(phaseDifference + time); | ||
- | | ||
- | // Add to total intensity | ||
- | totalIntensity += amplitude; | ||
- | | ||
- | // Store individual slit intensity | ||
- | individualIntensities.push(amplitude); | ||
- | } | ||
- | | ||
- | // Square the amplitude to get intensity | ||
- | const resultIntensity = Math.pow(totalIntensity, | ||
- | | ||
- | // Return the intensity and individual intensities | ||
- | return { | ||
- | total: resultIntensity, | ||
- | individual: individualIntensities.map(amp => Math.pow(amp, | ||
- | }; | ||
- | } | ||
- | | ||
- | // Draw the interference pattern | ||
- | function drawInterferencePattern() { | ||
- | const showIndividual = showIndividualCheckbox.checked; | ||
- | | ||
- | // Clear the pattern area | ||
- | ctx.fillStyle = ' | ||
- | ctx.fillRect(40, | ||
- | | ||
- | // Draw the intensity pattern | ||
- | const imageData = ctx.createImageData(canvas.width - 50, canvas.height); | ||
- | | ||
- | for (let x = 0; x < canvas.width - 50; x++) { | ||
- | for (let y = 0; y < canvas.height; | ||
- | const screenY = y; | ||
- | const { total, individual } = calculateIntensity(screenY); | ||
- | | ||
- | // Convert intensity to color (0-255) | ||
- | let intensity = Math.min(255, | ||
- | | ||
- | // Set pixel color based on position and intensity | ||
- | const pixelIndex = (y * (canvas.width - 50) + x) * 4; | ||
- | | ||
- | // Gradient color based on position and wavelength | ||
- | const wavelength = parseInt(wavelengthSlider.value); | ||
- | let r, g, b; | ||
- | | ||
- | // Simple wavelength to RGB approximation | ||
- | if (wavelength < 480) { | ||
- | // Blue to violet | ||
- | r = intensity * (wavelength - 400) / 80; | ||
- | g = 0; | ||
- | b = intensity; | ||
- | } else if (wavelength < 510) { | ||
- | // Blue to green | ||
- | r = 0; | ||
- | g = intensity * (wavelength - 480) / 30; | ||
- | b = intensity * (510 - wavelength) / 30; | ||
- | } else if (wavelength < 580) { | ||
- | // Green to yellow | ||
- | r = intensity * (wavelength - 510) / 70; | ||
- | g = intensity; | ||
- | b = 0; | ||
- | } else if (wavelength < 645) { | ||
- | // Yellow to red | ||
- | r = intensity; | ||
- | g = intensity * (645 - wavelength) / 65; | ||
- | b = 0; | ||
- | } else { | ||
- | // Red | ||
- | r = intensity; | ||
- | g = 0; | ||
- | b = 0; | ||
- | } | ||
- | | ||
- | // Add visual effect to illustrate interference pattern | ||
- | const distance = Math.abs(x - (canvas.width - 50) / 2); | ||
- | const falloff = 1 - Math.min(1, distance / 300); | ||
- | | ||
- | r *= falloff; | ||
- | g *= falloff; | ||
- | b *= falloff; | ||
- | | ||
- | imageData.data[pixelIndex] = r; | ||
- | imageData.data[pixelIndex + 1] = g; | ||
- | imageData.data[pixelIndex + 2] = b; | ||
- | imageData.data[pixelIndex + 3] = 255; | ||
- | } | ||
- | } | ||
- | | ||
- | ctx.putImageData(imageData, | ||
- | | ||
- | // Draw intensity graph on the right side | ||
- | ctx.strokeStyle = ' | ||
- | ctx.lineWidth = 2; | ||
- | ctx.beginPath(); | ||
- | | ||
- | const graphWidth = 150; | ||
- | const graphX = canvas.width - graphWidth; | ||
- | | ||
- | for (let y = 0; y < canvas.height; | ||
- | const { total, individual } = calculateIntensity(y); | ||
- | const intensity = total * graphWidth * 0.8; | ||
- | | ||
- | if (y === 0) { | ||
- | ctx.moveTo(graphX + intensity, y); | ||
- | } else { | ||
- | ctx.lineTo(graphX + intensity, y); | ||
- | } | ||
- | } | ||
- | | ||
- | ctx.stroke(); | ||
- | | ||
- | // Draw individual slit patterns if requested | ||
- | if (showIndividual) { | ||
- | const slitCount = parseInt(slitCountSlider.value); | ||
- | const colors = ['# | ||
- | | ||
- | for (let i = 0; i < slitCount; i++) { | ||
- | ctx.strokeStyle = colors[i % colors.length]; | ||
- | ctx.beginPath(); | ||
- | | ||
- | for (let y = 0; y < canvas.height; | ||
- | const { individual } = calculateIntensity(y); | ||
- | const intensity = individual[i] * graphWidth * 0.8; | ||
- | | ||
- | if (y === 0) { | ||
- | ctx.moveTo(graphX + intensity, y); | ||
- | } else { | ||
- | ctx.lineTo(graphX + intensity, y); | ||
- | } | ||
- | } | ||
- | | ||
- | ctx.stroke(); | ||
- | } | ||
- | } | ||
- | } | ||
- | | ||
- | // Animation function | ||
- | function animate() { | ||
- | time += 0.05; | ||
- | drawInterferencePattern(); | ||
- | animationId = requestAnimationFrame(animate); | ||
- | } | ||
- | | ||
- | // Update value displays | ||
- | function updateValueDisplays() { | ||
- | slitCountValue.textContent = slitCountSlider.value; | ||
- | slitWidthValue.textContent = slitWidthSlider.value; | ||
- | slitSeparationValue.textContent = slitSeparationSlider.value; | ||
- | wavelengthValue.textContent = wavelengthSlider.value; | ||
- | intensityValue.textContent = intensitySlider.value; | ||
- | } | ||
- | | ||
- | // Event listeners | ||
- | slitCountSlider.addEventListener(' | ||
- | updateValueDisplays(); | ||
- | drawSlits(); | ||
- | drawInterferencePattern(); | ||
- | }); | ||
- | | ||
- | slitWidthSlider.addEventListener(' | ||
- | updateValueDisplays(); | ||
- | drawSlits(); | ||
- | drawInterferencePattern(); | ||
- | }); | ||
- | | ||
- | slitSeparationSlider.addEventListener(' | ||
- | updateValueDisplays(); | ||
- | drawSlits(); | ||
- | drawInterferencePattern(); | ||
- | }); | ||
- | | ||
- | wavelengthSlider.addEventListener(' | ||
- | updateValueDisplays(); | ||
- | drawInterferencePattern(); | ||
- | }); | ||
- | | ||
- | intensitySlider.addEventListener(' | ||
- | updateValueDisplays(); | ||
- | drawInterferencePattern(); | ||
- | }); | ||
- | | ||
- | showIndividualCheckbox.addEventListener(' | ||
- | | ||
- | animateBtn.addEventListener(' | ||
- | if (isAnimating) { | ||
- | cancelAnimationFrame(animationId); | ||
- | animateBtn.textContent = 'Start Animation'; | ||
- | isAnimating = false; | ||
- | } else { | ||
- | animate(); | ||
- | animateBtn.textContent = 'Stop Animation'; | ||
- | isAnimating = true; | ||
- | } | ||
- | }); | ||
- | | ||
- | resetBtn.addEventListener(' | ||
- | // Reset sliders to default values | ||
- | slitCountSlider.value = 2; | ||
- | slitWidthSlider.value = 10; | ||
- | slitSeparationSlider.value = 100; | ||
- | wavelengthSlider.value = 500; | ||
- | intensitySlider.value = 100; | ||
- | showIndividualCheckbox.checked = false; | ||
- | | ||
- | // Stop animation if running | ||
- | if (isAnimating) { | ||
- | cancelAnimationFrame(animationId); | ||
- | animateBtn.textContent = 'Start Animation'; | ||
- | isAnimating = false; | ||
- | } | ||
- | | ||
- | // Reset time | ||
- | time = 0; | ||
- | | ||
- | // Update displays | ||
- | updateValueDisplays(); | ||
- | | ||
- | // Redraw | ||
- | drawSlits(); | ||
- | drawInterferencePattern(); | ||
- | }); | ||
- | | ||
- | // Initialize the visualization | ||
- | updateValueDisplays(); | ||
- | initCanvas(); | ||
- | </ | ||
- | </ | ||
- | </ | ||
- | |||