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:19] – 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: | ||
| </ | </ | ||
| </ | </ | ||
| - | |||
| - | |||
| - | < | ||
| - | <html lang=" | ||
| - | < | ||
| - | <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(); | ||
| - | </ | ||
| - | </ | ||
| - | </ | ||
| - | |||