Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,64 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Audio Harmonograph</title>
<title>Retro Harmonograph Live</title>
<link rel="stylesheet" href="style.css">

<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet">
</head>
<body>

<canvas id="screen"></canvas>

<div id="control-panel" class="retro-box">

<div class="panel-header">
<button id="minimizeBtn" class="control-icon" title="Minimize/Maximize">
_
</button>
Harmonograph Control Unit
</div>

<button id="audioBtn" class="retro-btn off">
◉ LIVE SIGNAL
</button>

<hr class="divider">

<div class="section-title">OSC PARAMETERS</div>

<label class="control-label">
Sensitivity
<input type="range" id="sensSlider" min="0.1" max="1.5" step="0.05" value="0.5">
</label>

<label class="control-label">
Decay (Damping)
<input type="range" id="dampSlider" min="0.0001" max="0.005" step="0.0001" value="0.003">
</label>

<label class="control-label">
Glow Intensity
<input type="range" id="glowSlider" min="0" max="30" step="1" value="12">
</label>

<hr class="divider">

<div class="section-title">VISUAL PRESETS</div>
<select id="styleSelect" class="retro-select">
<option value="CRT_GREEN"> CRT Classic</option>
<option value="NEON_BLUE"> Neon Blue</option>
<option value="VAPORWAVE"> Vaporwave</option>
<option value="ANALOG_RED"> Analog Red</option>
</select>

<div id="status-display" class="terminal-text">
STATUS: Ready. Press Live Signal to start.
</div>

</div>

<script type="module" src="main.js"></script>
</body>
</html>
</html>
79 changes: 72 additions & 7 deletions main.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,114 @@
// main.js - Application Orchestrator

// ================================
// 1. IMPORTS
// ================================

// Imports esențiale din ambele ramuri:
import { initializeControls } from './ui/controls.js';
import { initAudio, updateAudio, audioEngine, audioData, ensureAudioRunning } from "./audio/audioEngine.js";
import { generateHarmonographPoints } from "./harmonograph/harmonograph.js";
import { renderFrame } from "./render/renderer.js";

// Global variables
let canvas, ctx;
let started = false; // Necessar pentru controlul audio (din HEAD)

// Time variable for Harmonograph animation
let t = 0;
let started = false;

function resize() {

// ================================
// 2. CANVAS SETUP & RESIZE
// ================================

// Păstrăm funcția de setup din a7e96c4 pentru a inițializa canvas-ul
function setupCanvas() {
canvas = document.getElementById("screen");
ctx = canvas.getContext("2d");
resizeCanvas(); // Folosim resizeCanvas
window.addEventListener("resize", resizeCanvas);
}

// Renunțăm la resize() și păstrăm resizeCanvas() din a7e96c4
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}

// 🔥 Browserul NU permite audio până la un click/tap/tastă

// ================================
// 3. AUDIO UNLOCK & STARTUP (Logică preluată din HEAD)
// ================================

// 🔥 Browserul NU permite audio până la un click/tap/tastă.
const unlockAudio = async () => {
if (started) return;
started = true;
await start();
};
// Ascultători pentru deblocarea audio
window.addEventListener("pointerdown", unlockAudio, { once: true });
window.addEventListener("keydown", unlockAudio, { once: true });

async function start() {
canvas = document.getElementById("screen");
// Reinițializăm canvas-ul și contextul, deoarece setupCanvas() din a7e96c4 făcea asta
canvas = document.getElementById("screen");
ctx = canvas.getContext("2d");
resizeCanvas(); // Folosim resizeCanvas
window.addEventListener("resize", resizeCanvas);

resize();
window.addEventListener("resize", resize);

// 1. Initialize Audio
await initAudio();
await ensureAudioRunning();

// 2. Initialize UI Controls (din a7e96c4)
initializeControls();

// 3. Start the animation loop
loop();

// Mesaj de status (din a7e96c4)
console.log("Application initialized. Ready to render.");
}


// ================================
// 4. MAIN ANIMATION LOOP (Combinată)
// ================================

/**
* Main animation loop (runs at ~60 FPS).
*/
function loop() {
requestAnimationFrame(loop);

// 1. Audio Analysis (Din HEAD)
updateAudio();

const vol = audioData.volume || 0;

// 2. Update Time (Controls the slow, organic rotation of the figure) (Din HEAD)
// accelerăm timpul în funcție de audio
const speed = 0.5 + vol * 2.0 + audioData.energy * 1.2;
t += speed;

// 3. Drawing (Din HEAD)
const points = generateHarmonographPoints(t, canvas);

// 4. Render (Folosim renderFrame din HEAD/Renderer, care include efectul CRT)
renderFrame(ctx, points);
}


// ================================
// 5. INITIALIZATION FALLBACK (Modificat)
// ================================

// Păstrăm listenerul 'DOMContentLoaded' doar pentru logica UI care nu necesită audio,
// dar pornim bucla de animație în 'start()' (după click)
document.addEventListener('DOMContentLoaded', () => {
// setupCanvas și initializeControls sunt mutate în start()
// pentru a se asigura că funcționează după ce canvas-ul este gata.
// Lăsăm această secțiune goală, deoarece 'start' este punctul de intrare real.
});
Loading