ciaochris's picture
Update index.html
7579b4c verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Vers3Dynamics Cymatics</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/addons/p5.sound.min.js"></script>
<style>
body {
margin: 0; background: #050505; color: #ccc;
font-family: 'Courier New', Courier, monospace; overflow: hidden;
display: flex; flex-direction: column; align-items: center; justify-content: center;
height: 100vh;
/* FIX 1: Prevent bouncy scrolling on mobile */
touch-action: none;
}
canvas {
border: 1px solid #333;
box-shadow: 0 0 40px rgba(0, 255, 255, 0.05);
/* FIX 2: Ensure canvas never overlaps UI on small screens */
margin-bottom: 0px;
}
#ui-layer {
position: absolute; bottom: 20px; z-index: 10;
background: rgba(0, 0, 0, 0.9); backdrop-filter: blur(5px);
padding: 15px; border-radius: 12px; border: 1px solid #333;
display: flex; flex-direction: column; gap: 10px; width: 90%; max-width: 500px;
/* FIX 3: Ensure UI handles touches nicely */
touch-action: manipulation;
}
/* --- MOBILE SPECIFIC FIXES --- */
@media (max-width: 600px) {
body {
/* Shifts the visual center UP by adding "fake" padding at the bottom */
/* This leaves room for the UI so the canvas sits higher */
padding-bottom: 250px;
box-sizing: border-box;
}
#ui-layer {
bottom: 10px;
width: 95%;
padding: 10px;
}
button {
padding: 10px 5px; /* Larger touch targets */
}
}
.row { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
button { background: #111; color: cyan; border: 1px solid #333; padding: 8px 10px; cursor: pointer; border-radius: 4px; font-size: 11px; flex: 1; transition: all 0.2s; }
button:hover { border-color: cyan; background: #222; }
.active-btn { background: cyan; color: black; font-weight: bold; border-color: cyan; }
#physics-info {
height: 30px; font-size: 10px; color: #00ff00;
border-top: 1px solid #333; padding-top: 10px; margin-top: 5px; text-align: center;
font-family: monospace; letter-spacing: 1px;
}
input[type="text"] { background: #000; border: 1px solid #333; color: cyan; padding: 10px; width: 100%; box-sizing: border-box; border-radius: 4px; text-align: center;}
</style>
</head>
<body>
<div id="ui-layer">
<div class="row">
<button id="textModeBtn" class="active-btn">🅰️ Intention</button>
<button id="audioModeBtn">🅱️ Acoustic Injection</button>
</div>
<div id="textSection" class="row">
<input type="text" id="wordInput" placeholder="Type intention...">
</div>
<div id="audioSection" class="row" style="display:none;">
<input type="file" id="audioUpload" accept="audio/*" style="font-size:10px; color:#888;">
<button id="playBtn" disabled>▶ Play</button>
</div>
<div class="row">
<button id="physicsToggle">🛠️ Physics Sim</button>
<button id="freezeBtn">❄️ Freeze</button>
<button id="clearBtn">✨ Clear</button>
<button id="saveBtn">📸 Save</button>
</div>
<div id="physics-info">
SYSTEM READY
</div>
</div>
<script>
let m = 5, n = 7;
let particles = 3000;
let sensitivity = 0.02;
let physicsMode = false;
let currentMode = 'text';
let song, fft, isPlaying = false;
let frozenLayer, hasCheckpoint = false;
let canvasSize;
function setup() {
calculateCanvasSize();
let cnv = createCanvas(canvasSize, canvasSize);
pixelDensity(2);
colorMode(HSB, 360, 100, 100, 100);
background(0);
fft = new p5.FFT(0.8);
frozenLayer = createGraphics(width, height);
frozenLayer.colorMode(HSB, 360, 100, 100, 100);
setupButtons();
windowResized();
}
function calculateCanvasSize() {
// Desktop: 90% of smallest dimension
// Mobile: 95% of Width (fills width) OR fit within available top height
if (windowWidth < 600) {
canvasSize = min(windowWidth * 0.95, windowHeight - 300);
} else {
canvasSize = min(windowWidth, windowHeight) * 0.9;
}
}
function windowResized() {
calculateCanvasSize();
resizeCanvas(canvasSize, canvasSize);
background(0);
}
function setupButtons() {
select('#physicsToggle').mousePressed(() => {
physicsMode = !physicsMode;
let btn = select('#physicsToggle');
if(physicsMode) {
btn.addClass('active-btn');
btn.html("🛠️ Simulating...");
} else {
btn.removeClass('active-btn');
btn.html("🛠️ Physics Sim");
}
updatePhysicsText();
});
select('#wordInput').input(function() {
let word = this.value();
if(word.length === 0) return;
let v1 = 0, v2 = 0;
for (let i = 0; i < word.length; i++) {
v1 += word.charCodeAt(i); v2 += word.charCodeAt(i) * (i + 1);
}
m = map(v1 % 100, 0, 100, 2, 12);
n = map(v2 % 100, 0, 100, 2, 12);
if(!hasCheckpoint) background(0);
});
select('#textModeBtn').mousePressed(() => switchMode('text'));
select('#audioModeBtn').mousePressed(() => switchMode('audio'));
select('#audioUpload').elt.onchange = (e) => {
if(song) song.stop();
song = loadSound(e.target.files[0], () => {
select('#playBtn').removeAttribute('disabled').html("▶ Play");
select('#physics-info').html("AUDIO BUFFER LOADED");
});
};
select('#playBtn').mousePressed(() => {
if(song.isPlaying()) { song.pause(); isPlaying = false; select('#playBtn').html("▶ Play"); }
else { song.play(); isPlaying = true; select('#playBtn').html("⏸ Pause"); }
});
select('#freezeBtn').mousePressed(() => {
frozenLayer.clear();
frozenLayer.image(get(), 0, 0, width, height);
hasCheckpoint = true;
select('#freezeBtn').html("❄️ Saved");
setTimeout(() => select('#freezeBtn').html("❄️ Freeze"), 1000);
});
select('#clearBtn').mousePressed(() => {
if(hasCheckpoint) image(frozenLayer, 0, 0, width, height);
else background(0);
});
select('#saveBtn').mousePressed(() => saveCanvas('Vers3Dynamics_Blueprint', 'png'));
}
function switchMode(m) {
currentMode = m;
select('#textSection').style('display', m === 'text' ? 'flex' : 'none');
select('#audioSection').style('display', m === 'audio' ? 'flex' : 'none');
select('#textModeBtn').toggleClass('active-btn', m === 'text');
select('#audioModeBtn').toggleClass('active-btn', m === 'audio');
}
function updatePhysicsText() {
if (physicsMode) {
let traps = floor(m * n * 2);
let stability = ((m+n)/24*100).toFixed(1);
select('#physics-info').html(`LEVITATION TRAPS: ${traps} | PRESSURE: HIGH`);
} else {
select('#physics-info').html(":: VISUAL RESONANCE ENGINE ::");
}
}
function draw() {
if (currentMode === 'audio' && isPlaying) {
fft.analyze();
m = lerp(m, map(fft.getEnergy("bass"), 0, 255, 2, 10), 0.05);
n = lerp(n, map(fft.getEnergy("treble"), 0, 255, 2, 14), 0.05);
background(0, 0, 0, 15);
}
if (physicsMode) {
if (frameCount % 5 === 0) {
for(let k=0; k<10; k++) {
let px = random(width);
let py = random(height);
let lx = map(px, 0, width, -1, 1);
let ly = map(py, 0, height, -1, 1);
let pVal = cos(n * PI * lx / 2) * cos(m * PI * ly / 2) - cos(m * PI * lx / 2) * cos(n * PI * ly / 2);
if(abs(pVal) > 0.5) {
noFill(); stroke(0, 100, 100, 10); ellipse(px, py, 10 + abs(pVal)*20);
}
}
}
if (frameCount % 20 === 0) {
stroke(120, 100, 100, 40); strokeWeight(1);
let step = width/15;
for(let gx=0; gx<width; gx+=step) {
for(let gy=0; gy<height; gy+=step) {
let lx = map(gx, 0, width, -1, 1);
let ly = map(gy, 0, height, -1, 1);
let val = cos(n * PI * lx / 2) * cos(m * PI * ly / 2) - cos(m * PI * lx / 2) * cos(n * PI * ly / 2);
if(abs(val) < 0.05) {
line(gx-3, gy, gx+3, gy); line(gx, gy-3, gx, gy+3);
}
}
}
}
}
if (frameCount % 30 === 0) updatePhysicsText();
for (let i = 0; i < particles; i++) {
let x = random(-1, 1), y = random(-1, 1);
let val = cos(n * PI * x / 2) * cos(m * PI * y / 2) - cos(m * PI * x / 2) * cos(n * PI * y / 2);
if (abs(val) < sensitivity) {
let sx = map(x, -1, 1, 0, width), sy = map(y, -1, 1, 0, height);
stroke(physicsMode ? color(120, 100, 100, 90) : color((frameCount * 0.5 + dist(0,0,x,y) * 100) % 360, 80, 100, 80));
strokeWeight(1.5);
point(sx, sy);
}
}
}
</script>
</body>
</html>