MAMware / do

0 stars 0 forks source link

Web Synth #1

Open MAMware opened 3 weeks ago

MAMware commented 3 weeks ago

Uses device sensors aligned with a Linear Feedback Shift Register (LFSR) to create dynamic sound patterns on a browser ala C64 style (hopefully).

Co-created with Microsoft copilot

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Commodore 64 Emulator</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div id="screen"></div>
    <script src="script.js"></script>
</body>
</html>

styles.css

body {
    background-color: #0000AA;
    color: #FFFFFF;
    font-family: 'C64', monospace;
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    transition: background-color 0.5s, color 0.5s;
}

#screen {
    width: 80%;
    max-width: 800px;
    padding: 20px;
    background-color: #0000AA;
    border: 2px solid #FFFFFF;
    box-sizing: border-box;
}

.blink {
    animation: blink 1s step-end infinite;
}

@keyframes blink {
    from, to {
        color: transparent;
    }
    50% {
        color: #FFFFFF;
    }
}

script.js

document.addEventListener('DOMContentLoaded', () => {
    const screen = document.getElementById('screen');

    // Create an Audio Context
    var audioContext = new (window.AudioContext || window.webkitAudioContext)();

    // Create Oscillator Nodes
    var oscillator1 = audioContext.createOscillator();
    var oscillator2 = audioContext.createOscillator();
    var oscillator3 = audioContext.createOscillator();

    // Set oscillator types (sine, square, sawtooth, triangle)
    oscillator1.type = 'square';
    oscillator2.type = 'sawtooth';
    oscillator3.type = 'triangle';

    // Create Gain Nodes for volume control
    var gainNode1 = audioContext.createGain();
    var gainNode2 = audioContext.createGain();
    var gainNode3 = audioContext.createGain();

    // Connect the oscillators to the gain nodes
    oscillator1.connect(gainNode1);
    oscillator2.connect(gainNode2);
    oscillator3.connect(gainNode3);

    // Connect the gain nodes to the destination (speakers)
    gainNode1.connect(audioContext.destination);
    gainNode2.connect(audioContext.destination);
    gainNode3.connect(audioContext.destination);

    // Start the oscillators
    oscillator1.start();
    oscillator2.start();
    oscillator3.start();

    // Function to handle device orientation
    function handleOrientation(event) {
        var alpha = event.alpha;
        var beta = event.beta;
        var gamma = event.gamma;

        // Convert the alpha, beta, and gamma values to RGB values
        var red = Math.abs(Math.floor((alpha / 360) * 255));
        var green = Math.abs(Math.floor((beta / 180) * 255));
        var blue = Math.abs(Math.floor((gamma / 90) * 255));

        // Set the background color for the body
        var backgroundColor = `rgb(${red}, ${green}, ${blue})`;
        document.body.style.backgroundColor = backgroundColor;

        // Calculate the brightness to determine text color
        var brightness = (red * 299 + green * 587 + blue * 114) / 1000;
        var textColor = brightness > 123 ? 'rgb(32, 32, 32)' : 'rgb(255, 255, 255)';

        // Set the text color for the body
        document.body.style.color = textColor;

        // Update the oscillator frequencies based on device orientation
        var baseFrequency = 80; // Base frequency in Hz
        var frequency1 = baseFrequency + (beta * 2); // Adjust the multiplier as needed
        var frequency2 = baseFrequency * 1.25 + (gamma * 2); // Major third
        var frequency3 = baseFrequency * 1.5 + (alpha * 2); // Perfect fifth

        oscillator1.frequency.setValueAtTime(frequency1, audioContext.currentTime);
        oscillator2.frequency.setValueAtTime(frequency2, audioContext.currentTime);
        oscillator3.frequency.setValueAtTime(frequency3, audioContext.currentTime);
    }

    // Check for device orientation permission
    if (typeof DeviceOrientationEvent.requestPermission === 'function') {
        DeviceOrientationEvent.requestPermission()
            .then(permissionState => {
                if (permissionState === 'granted') {
                    window.addEventListener('deviceorientation', handleOrientation);
                } else {
                    alert('Permission to access device orientation was denied.');
                }
            })
            .catch(console.error);
    } else {
        // Handle non-iOS 13+ devices
        window.addEventListener('deviceorientation', handleOrientation);
    }
});

SelectingMultipleDOMElements from https://github.com/StephenMercer/

MAMware commented 2 weeks ago

Revision v0.2 Revised code with the adjustments organized to ensure proper initialization and performance:

document.addEventListener('DOMContentLoaded', () => {
    const screen = document.getElementById('screen');

    // Create an Audio Context
    let audioContext;

    // Function to initialize Audio Context and start oscillators
    function initializeAudio() {
        if (!audioContext) {
            audioContext = new (window.AudioContext || window.webkitAudioContext)();

            // Create Oscillator Nodes
            var oscillator1 = audioContext.createOscillator();
            var oscillator2 = audioContext.createOscillator();
            var oscillator3 = audioContext.createOscillator();

            // Set oscillator types (sine, square, sawtooth, triangle)
            oscillator1.type = 'square';
            oscillator2.type = 'sawtooth';
            oscillator3.type = 'triangle';

            // Create Gain Nodes for volume control
            var gainNode1 = audioContext.createGain();
            var gainNode2 = audioContext.createGain();
            var gainNode3 = audioContext.createGain();

            // Connect the oscillators to the gain nodes
            oscillator1.connect(gainNode1);
            oscillator2.connect(gainNode2);
            oscillator3.connect(gainNode3);

            // Connect the gain nodes to the destination (speakers)
            gainNode1.connect(audioContext.destination);
            gainNode2.connect(audioContext.destination);
            gainNode3.connect(audioContext.destination);

            // Set initial gain values
            gainNode1.gain.setValueAtTime(0.5, audioContext.currentTime);
            gainNode2.gain.setValueAtTime(0.5, audioContext.currentTime);
            gainNode3.gain.setValueAtTime(0.5, audioContext.currentTime);

            // Start the oscillators
            oscillator1.start();
            oscillator2.start();
            oscillator3.start();
        }
    }

    // Function to handle device orientation
    function handleOrientation(event) {
        var alpha = event.alpha;
        var beta = event.beta;
        var gamma = event.gamma;

        // Convert the alpha, beta, and gamma values to RGB values
        var red = Math.abs(Math.floor((alpha / 360) * 255));
        var green = Math.abs(Math.floor((beta / 180) * 255));
        var blue = Math.abs(Math.floor((gamma / 90) * 255));

        // Update the oscillator frequencies based on device orientation
        var baseFrequency = 80; // Base frequency in Hz
        var frequency1 = baseFrequency + (beta * 2); // Adjust the multiplier as needed
        var frequency2 = baseFrequency * 1.25 + (gamma * 2); // Major third
        var frequency3 = baseFrequency * 1.5 + (alpha * 2); // Perfect fifth

        oscillator1.frequency.setValueAtTime(frequency1, audioContext.currentTime);
        oscillator2.frequency.setValueAtTime(frequency2, audioContext.currentTime);
        oscillator3.frequency.setValueAtTime(frequency3, audioContext.currentTime);

        // Use requestAnimationFrame for color updates
        requestAnimationFrame(() => {
            var backgroundColor = `rgb(${red}, ${green}, ${blue})`;
            document.body.style.backgroundColor = backgroundColor;

            var brightness = (red * 299 + green * 587 + blue * 114) / 1000;
            var textColor = brightness > 123 ? 'rgb(32, 32, 32)' : 'rgb(255, 255, 255)';
            document.body.style.color = textColor;
        });
    }

    // Check for device orientation permission
    if (typeof DeviceOrientationEvent.requestPermission === 'function') {
        DeviceOrientationEvent.requestPermission()
            .then(permissionState => {
                if (permissionState === 'granted') {
                    window.addEventListener('deviceorientation', handleOrientation);
                } else {
                    alert('Permission to access device orientation was denied.');
                }
            })
            .catch(console.error);
    } else {
        // Handle non-iOS 13+ devices
        window.addEventListener('deviceorientation', handleOrientation);
    }

    // Initialize audio context on user interaction
    document.addEventListener('click', initializeAudio);
});

This version ensures that the AudioContext is initialized after a user interaction and that the oscillators start correctly. It also optimizes the color updates using requestAnimationFrame.

hints to debug:

  1. Separate Concerns: Temporarily disable the color-changing code to see if the audio works without it. This will help you determine if the color changes are indeed causing the issue.

    // Comment out the color-changing code
    // document.body.style.backgroundColor = backgroundColor;
    // document.body.style.color = textColor;
  2. Optimize Code Execution: Ensure that the color changes and audio updates are not happening too frequently, which could cause performance issues. You can use requestAnimationFrame to optimize the updates.

    function handleOrientation(event) {
        var alpha = event.alpha;
        var beta = event.beta;
        var gamma = event.gamma;
    
        // Convert the alpha, beta, and gamma values to RGB values
        var red = Math.abs(Math.floor((alpha / 360) * 255));
        var green = Math.abs(Math.floor((beta / 180) * 255));
        var blue = Math.abs(Math.floor((gamma / 90) * 255));
    
        // Update the oscillator frequencies based on device orientation
        var baseFrequency = 80; // Base frequency in Hz
        var frequency1 = baseFrequency + (beta * 2); // Adjust the multiplier as needed
        var frequency2 = baseFrequency * 1.25 + (gamma * 2); // Major third
        var frequency3 = baseFrequency * 1.5 + (alpha * 2); // Perfect fifth
    
        oscillator1.frequency.setValueAtTime(frequency1, audioContext.currentTime);
        oscillator2.frequency.setValueAtTime(frequency2, audioContext.currentTime);
        oscillator3.frequency.setValueAtTime(frequency3, audioContext.currentTime);
    
        // Use requestAnimationFrame for color updates
        requestAnimationFrame(() => {
            var backgroundColor = `rgb(${red}, ${green}, ${blue})`;
            document.body.style.backgroundColor = backgroundColor;
    
            var brightness = (red * 299 + green * 587 + blue * 114) / 1000;
            var textColor = brightness > 123 ? 'rgb(32, 32, 32)' : 'rgb(255, 255, 255)';
            document.body.style.color = textColor;
        });
    }
  3. Debugging: Add some console logs to see if the color changes and audio updates are happening as expected.

    function handleOrientation(event) {
        var alpha = event.alpha;
        var beta = event.beta;
        var gamma = event.gamma;
    
        console.log(`Alpha: ${alpha}, Beta: ${beta}, Gamma: ${gamma}`);
    
        // Existing code for color changes and audio updates
    }

Try these steps and see if they help isolate the issue.