anycoder-42e9de3b / index.html
HI7RAI's picture
Upload folder using huggingface_hub
61e2495 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>ProCalibrated FX Studio</title>
<!-- Font Awesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary: #00f2ff;
--secondary: #ff0055;
--bg-dark: #0a0a0f;
--panel-bg: rgba(20, 20, 30, 0.85);
--text-main: #e0e0e0;
--grid-color: rgba(0, 242, 255, 0.1);
}
* { box-sizing: border-box; outline: none; }
body {
margin: 0;
font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
background-color: var(--bg-dark);
color: var(--text-main);
overflow: hidden;
height: 100vh;
display: flex;
flex-direction: column;
}
/* Header */
header {
padding: 10px 20px;
background: #000;
border-bottom: 1px solid #333;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 100;
}
h1 { font-size: 1.2rem; margin: 0; text-transform: uppercase; letter-spacing: 2px; color: var(--primary); }
.anycoder-link {
font-size: 0.8rem;
color: #888;
text-decoration: none;
}
.anycoder-link:hover { color: var(--primary); }
/* Main Layout */
.studio-container {
display: grid;
grid-template-columns: 350px 1fr 300px;
flex: 1;
height: calc(100vh - 50px);
}
/* Panels */
.panel {
background: var(--panel-bg);
border-right: 1px solid #333;
padding: 15px;
overflow-y: auto;
backdrop-filter: blur(10px);
}
.panel-right {
border-left: 1px solid #333;
border-right: none;
}
h2 {
font-size: 0.9rem;
border-bottom: 1px solid #444;
padding-bottom: 5px;
margin-top: 20px;
color: var(--secondary);
}
h2:first-child { margin-top: 0; }
/* Controls */
.control-group { margin-bottom: 15px; }
label { display: block; font-size: 0.8rem; margin-bottom: 5px; color: #aaa; }
input[type="range"] {
width: 100%;
height: 6px;
background: #333;
border-radius: 3px;
-webkit-appearance: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
background: var(--primary);
border-radius: 50%;
cursor: pointer;
}
input[type="number"], input[type="text"], input[type="file"] {
width: 100%;
background: #222;
border: 1px solid #444;
color: white;
padding: 5px;
border-radius: 4px;
}
.btn {
background: #333;
color: white;
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
width: 100%;
margin-top: 5px;
transition: 0.2s;
}
.btn:hover { background: #444; }
.btn-primary { background: var(--primary); color: #000; font-weight: bold; }
.btn-primary:hover { background: #00dce6; }
/* Viewport / World */
.viewport {
position: relative;
display: flex;
justify-content: center;
align-items: center;
background: radial-gradient(circle at center, #1a1a2e 0%, #000 100%);
perspective: 1000px; /* For 3D */
overflow: hidden;
}
/* The World Container (Gyro Target) */
#world {
width: 640px;
height: 360px;
position: relative;
transform-style: preserve-3d;
transition: transform 0.1s linear;
box-shadow: 0 0 50px rgba(0,0,0,0.8);
}
canvas {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: contain;
}
/* Overlays */
#rasterOverlay {
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
background-image:
linear-gradient(var(--grid-color) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
background-size: 50px 50px;
pointer-events: none;
z-index: 10;
display: none;
border: 1px solid var(--primary);
}
.hud-info {
position: absolute;
top: 10px;
left: 10px;
font-family: monospace;
color: var(--primary);
z-index: 20;
text-shadow: 0 0 5px var(--primary);
pointer-events: none;
}
.math-overlay {
position: absolute;
bottom: 20px;
right: 20px;
font-size: 3rem;
font-family: 'Times New Roman', serif;
font-style: italic;
color: rgba(255,255,255,0.2);
z-index: 15;
pointer-events: none;
}
/* BPM Visualizer */
.bpm-bars {
display: flex;
gap: 2px;
height: 50px;
align-items: flex-end;
margin-top: 10px;
}
.bar {
flex: 1;
background: var(--secondary);
transition: height 0.1s;
}
/* Gyro Controls UI */
.gyro-data {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 10px;
font-family: monospace;
font-size: 0.8rem;
}
.gyro-val { color: var(--primary); }
/* Responsive */
@media (max-width: 1000px) {
.studio-container {
grid-template-columns: 1fr;
grid-template-rows: 1fr auto;
overflow-y: auto;
}
.panel { height: 300px; border-right: none; border-top: 1px solid #333; }
.viewport { min-height: 300px; }
}
</style>
</head>
<body>
<header>
<h1><i class="fas fa-cube"></i> FX Lab</h1>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">Built with anycoder</a>
</header>
<div class="studio-container">
<!-- LEFT PANEL: Inputs & Global FX -->
<aside class="panel">
<h2><i class="fas fa-photo-video"></i> Source Loader</h2>
<div class="control-group">
<label>Scene 1 (Video/Image)</label>
<input type="file" id="fileA" accept="image/*,video/*">
</div>
<div class="control-group">
<label>Scene 2 (Video/Image)</label>
<input type="file" id="fileB" accept="image/*,video/*">
</div>
<h2><i class="fas fa-cut"></i> Cutter / Timing</h2>
<div class="control-group">
<label>Scene 1 End Time (sec)</label>
<input type="number" id="cut1End" value="5">
</div>
<div class="control-group">
<label>Scene 2 End Time (sec)</label>
<input type="number" id="cut2End" value="10">
</div>
<div class="control-group">
<label>Global Time (Manual)</label>
<input type="range" id="timeSlider" min="0" max="20" step="0.1" value="0">
<button class="btn btn-primary" id="playBtn"><i class="fas fa-play"></i> Play/Pause</button>
</div>
<h2><i class="fas fa-magic"></i> FX Engine</h2>
<div class="control-group">
<label>Saturation</label>
<input type="range" class="fx-input" data-prop="saturate" min="0" max="300" value="100">
</div>
<div class="control-group">
<label>Contrast</label>
<input type="range" class="fx-input" data-prop="contrast" min="0" max="300" value="100">
</div>
<div class="control-group">
<label>Brightness (Light)</label>
<input type="range" class="fx-input" data-prop="brightness" min="0" max="200" value="100">
</div>
<div class="control-group">
<label>Sepia</label>
<input type="range" class="fx-input" data-prop="sepia" min="0" max="100" value="0">
</div>
<div class="control-group">
<label>Blur / Dust FX</label>
<input type="range" id="dustAmount" min="0" max="50" value="0">
</div>
</aside>
<!-- CENTER PANEL: Viewport -->
<section class="viewport" id="viewportContainer">
<div id="world">
<!-- HUD -->
<div class="hud-info">
FPS: <span id="fpsCounter">0</span><br>
<span style="color:var(--secondary)">π³ ≈ <span id="piVal"></span></span>
</div>
<!-- Math Overlay -->
<div class="math-overlay">π³</div>
<!-- Main Rendering Canvas -->
<canvas id="mainCanvas" width="1280" height="720"></canvas>
<!-- Raster Grid -->
<div id="rasterOverlay"></div>
</div>
</section>
<!-- RIGHT PANEL: Calibration, Audio, Gyro -->
<aside class="panel panel-right">
<h2><i class="fas fa-border-all"></i> Raster & Align</h2>
<div class="control-group">
<label>Show Raster</label>
<input type="checkbox" id="toggleRaster">
</div>
<div class="control-group">
<label>Offset X (Pan)</label>
<input type="range" id="alignX" min="-200" max="200" value="0">
</div>
<div class="control-group">
<label>Offset Y (Tilt)</label>
<input type="range" id="alignY" min="-200" max="200" value="0">
</div>
<div class="control-group">
<label>Zoom / Scale</label>
<input type="range" id="alignZoom" min="50" max="200" value="100">
</div>
<h2><i class="fas fa-music"></i> Audio & BPM</h2>
<div class="control-group">
<label>Load Audio</label>
<input type="file" id="audioFile" accept="audio/*">
</div>
<div class="control-group">
<label>BPM Value</label>
<input type="number" id="bpmInput" value="120">
</div>
<div class="bpm-bars" id="visualizer">
<!-- Bars generated by JS -->
</div>
<h2><i class="fas fa-mobile-alt"></i> Gyro Control</h2>
<div class="control-group">
<button class="btn btn-primary" id="reqPermission"><i class="fas fa-broadcast-tower"></i> Init Gyro (iOS)</button>
<div class="gyro-data">
<div>X: <span id="hudX" class="gyro-val">0</span>°</div>
<div>Y: <span id="hudY" class="gyro-val">0</span>°</div>
</div>
<label style="margin-top:10px">Multiplier</label>
<input type="range" id="speedMult" min="0.1" max="5" step="0.1" value="1">
</div>
<div class="control-group">
<button class="btn" onclick="toggleLock('x')" id="lockXBtn">X-Achse</button>
<button class="btn" onclick="toggleLock('y')" id="lockYBtn">Y-Achse</button>
</div>
</aside>
</div>
<!-- LOGIC -->
<script>
/**
* MATH CONSTANTS & SETUP
*/
const PI_CUBED = Math.pow(Math.PI, 3); // ~31.006
document.getElementById('piVal').innerText = PI_CUBED.toFixed(4);
// Canvas Setup
const canvas = document.getElementById('mainCanvas');
const ctx = canvas.getContext('2d');
const world = document.getElementById('world');
// State
const state = {
isPlaying: false,
currentTime: 0,
cut1End: 5,
cut2End: 10,
mediaA: null, // { type: 'img'|'video', element: DOMNode }
mediaB: null,
fx: { saturate: 100, contrast: 100, brightness: 100, sepia: 0 },
raster: { show: false, x: 0, y: 0, zoom: 1 },
gyro: {
currentX: 0, currentY: 0,
baseX: null, baseY: null,
lockedX: false, lockedY: false,
fixedValX: 0, fixedValY: 0,
multiplier: 1
},
particles: [],
audioCtx: null,
audioSource: null,
audioBuffer: null,
analyser: null,
bpm: 120
};
// Generate Visualizer Bars
const visualizerContainer = document.getElementById('visualizer');
for(let i=0; i<16; i++){
let bar = document.createElement('div');
bar.className = 'bar';
bar.style.height = '10%';
visualizerContainer.appendChild(bar);
}
const bars = document.querySelectorAll('.bar');
/**
* 1. CORE RENDER LOOP
*/
let lastTime = 0;
function animate(timestamp) {
if (!lastTime) lastTime = timestamp;
const delta = timestamp - lastTime;
lastTime = timestamp;
// FPS
document.getElementById('fpsCounter').innerText = Math.round(1000/delta);
// Time Management
if (state.isPlaying) {
state.currentTime += delta / 1000;
document.getElementById('timeSlider').value = state.currentTime;
// Loop logic
if (state.currentTime > state.cut2End) state.currentTime = 0;
}
// Clear Canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw Media (Cutter Logic)
// Apply Global FX Filters via Context Filter (Modern Browsers)
ctx.filter = `saturate(${state.fx.saturate}%) contrast(${state.fx.contrast}%) brightness(${state.fx.brightness}%) sepia(${state.fx.sepia}%)`;
// Logic: If time < cut1, show A. Else show B.
let activeMedia = null;
if (state.currentTime < state.cut1End && state.mediaA) {
activeMedia = state.mediaA;
} else if (state.currentTime >= state.cut1End && state.currentTime < state.cut2End && state.mediaB) {
activeMedia = state.mediaB;
} else if (state.mediaA) {
activeMedia = state.mediaA; // Fallback or loop
}
if (activeMedia) {
// Draw active media
if (activeMedia.type === 'video') {
if (!activeMedia.element.paused && !activeMedia.element.ended) {
// Sync video current time if playing,
// but in this app, 'currentTime' controls the playback position of the "Editor"
// For simplicity in this playground, we let the video play naturally or seek.
// To sync perfectly:
// activeMedia.element.currentTime = state.currentTime; // Force seek (may stutter)
}
// Center and Scale based on alignment
const dw = canvas.width * state.raster.zoom;
const dh = canvas.height * state.raster.zoom;
const dx = (canvas.width - dw) / 2 + state.raster.x;
const dy = (canvas.height - dh) / 2 + state.raster.y;
ctx.drawImage(activeMedia.element, dx, dy, dw, dh);
} else if (activeMedia.type === 'img') {
const dw = canvas.width * state.raster.zoom;
const dh = canvas.height * state.raster.zoom;
const dx = (canvas.width - dw) / 2 + state.raster.x;
const dy = (canvas.height - dh) / 2 + state.raster.y;
ctx.drawImage(activeMedia.element, dx, dy, dw, dh);
}
} else {
// Placeholder Text
ctx.fillStyle = "#333";
ctx.font = "20px Arial";
ctx.fillText("No Media Loaded", 50, 50);
}
ctx.filter = 'none'; // Reset filter for particles
// FX: Dust / Particles
updateParticles(delta);
// FX: Audio Visualizer (Overlay on Canvas)
drawAudioVisualizer();
requestAnimationFrame(animate);
}
/**
* 2. PARTICLE SYSTEM (Dust/FX)
*/
function initParticles() {
for(let i=0; i<50; i++) {
state.particles.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
vx: (Math.random() - 0.5) * 0.5,
vy: (Math.random() - 0.5) * 0.5,
size: Math.random() * 2,
alpha: Math.random()
});
}
}
initParticles();
function updateParticles(delta) {
// Check dust slider
const dustAmount = document.getElementById('dustAmount').value;
state.particles.forEach(p => {
p.x += p.vx;
p.y += p.vy;
// Wrap around
if(p.x < 0) p.x = canvas.width;
if(p.x > canvas.width) p.x = 0;
if(p.y < 0) p.y = canvas.height;
if(p.y > canvas.height) p.y = 0;
// Draw
ctx.fillStyle = `rgba(200, 200, 255, ${p.alpha * (dustAmount/100)})`;
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI*2);
ctx.fill();
});
}
/**
* 3. AUDIO & BPM
*/
document.getElementById('audioFile').addEventListener('change', function(e) {
const file = e.target.files[0];
if(!file) return;
const reader = new FileReader();
reader.onload = function(ev) {
if(!state.audioCtx) {
state.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
state.audioCtx.decodeAudioData(ev.target.result, function(buffer) {
state.audioBuffer = buffer;
playAudio();
});
};
reader.readAsArrayBuffer(file);
});
function playAudio() {
if(state.audioSource) state.audioSource.stop();
state.audioSource = state.audioCtx.createBufferSource();
state.audioSource.buffer = state.audioBuffer;
state.analyser = state.audioCtx.createAnalyser();
state.analyser.fftSize = 32;
state.audioSource.connect(state.analyser);
state.analyser.connect(state.audioCtx.destination);
state.audioSource.start(0);
}
function drawAudioVisualizer() {
if (!state.analyser) return;
const bufferLength = state.analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);
state.analyser.getByteFrequencyData(dataArray);
// Sync bars to DOM
for(let i=0; i<16; i++) {
// Average chunks of frequency data
let sum = 0;
const step = Math.floor(bufferLength / 16);
for(let j=0; j<step; j++) {
sum += dataArray[i*step + j];
}
const avg = sum / step;
// Scale based on BPM pulse (fake math for visual flair)
const height = (avg / 255) * 100;
bars[i].style.height = `${height}%`;
// Colorize on beat (simplified)
if(height > 80) bars[i].style.background = 'white';
else bars[i].style.background = 'var(--secondary)';
}
}
/**
* 4. MEDIA LOADER
*/
function handleFileSelect(e, slot) {
const file = e.target.files[0];
if(!file) return;
const url = URL.createObjectURL(file);
const type = file.type.startsWith('video') ? 'video' : 'img';
let element;
if(type === 'video') {
element = document.createElement('video');
element.src = url;
element.muted = true;
element.loop = true;
// Note: We don't play() globally here to allow manual scrubbing,
// but we could.
} else {
element = new Image();
element.src = url;
}
element.onloadeddata = () => {
state[slot] = { type, element };
};
}
document.getElementById('fileA').addEventListener('change', (e) => handleFileSelect(e, 'mediaA'));
document.getElementById('fileB').addEventListener('change', (e) => handleFileSelect(e, 'mediaB'));
/**
* 5. CONTROLS WIRING
*/
document.getElementById('playBtn').addEventListener('click', () => {
state.isPlaying = !state.isPlaying;
// If video exists, we could sync play/pause
});
document.querySelectorAll('.fx-input').forEach(input => {
input.addEventListener('input', (e) => {
state.fx[e.target.dataset.prop] = e.target.value;
});
});
document.getElementById('timeSlider').addEventListener('input', (e) => {
state.currentTime = parseFloat(e.target.value);
// Seek video if exists
if(state.mediaA && state.mediaA.type === 'video') state.mediaA.element.currentTime = state.currentTime;
});
document.getElementById('toggleRaster').addEventListener('change', (e) => {
document.getElementById('rasterOverlay').style.display = e.target.checked ? 'block' : 'none';
});
document.getElementById('alignX').addEventListener('input', (e) => state.raster.x = parseInt(e.target.value));
document.getElementById('alignY').addEventListener('input', (e) => state.raster.y = parseInt(e.target.value));
document.getElementById('alignZoom').addEventListener('input', (e) => state.raster.zoom = parseInt(e.target.value)/100);
/**
* 6. GYRO INTEGRATION (Based on prompt snippet logic)
*/
const speedInput = document.getElementById('speedMult');
const hudX = document.getElementById('hudX');
const hudY = document.getElementById('hudY');
speedInput.addEventListener('input', (e) => {
state.gyro.multiplier = parseFloat(e.target.value);
});
function toggleLock(axis) {
const btn = document.getElementById(axis === 'x' ? 'lockXBtn' : 'lockYBtn');
if(axis === 'x') {
state.gyro.lockedX = !state.gyro.lockedX;
if(state.gyro.lockedX) {
state.gyro.fixedValX = state.gyro.currentX;
btn.classList.add('locked');
btn.style.borderColor = "var(--secondary)";
} else {
btn.classList.remove('locked');
btn.style.borderColor = "#444";
}
} else {
state.gyro.lockedY = !state.gyro.lockedY;
if(state.gyro.lockedY) {
state.gyro.fixedValY = state.gyro.currentY;
btn.classList.add('locked');
btn.style.borderColor = "var(--secondary)";
} else {
btn.classList.remove('locked');
btn.style.borderColor = "#444";
}
}
}
function handleOrientation(event) {
const xRaw = event.beta || 0; // X axis (beta)
const yRaw = event.gamma || 0; // Y axis (gamma)
if (state.gyro.baseX === null) {
state.gyro.baseX = xRaw;
state.gyro.baseY = yRaw;
}
let newX = (xRaw - state.gyro.baseX) * state.gyro.multiplier;
let newY = (yRaw - state.gyro.baseY) * state.gyro.multiplier;
// Logic check: locked or update
if (!state.gyro.lockedX) state.gyro.currentX = newX;
else state.gyro.currentX = state.gyro.fixedValX;
if (!state.gyro.lockedY) state.gyro.currentY = newY;
else state.gyro.currentY = state.gyro.fixedValY;
updateGyroView();
}
// Mouse fallback for desktop testing
document.addEventListener('mousemove', (e) => {
if(!window.DeviceOrientationEvent || !('ontouchstart' in window)) {
// Desktop simulation
const xPct = (e.clientX / window.innerWidth) - 0.5;
const yPct = (e.clientY / window.innerHeight) - 0.5;
// Only update if not locked (simulated logic)
// Simplified for desktop demo
if(!state.gyro.lockedX) state.gyro.currentX = yPct * 180 * state.gyro.multiplier;
if(!state.gyro.lockedY) state.gyro.currentY = xPct * 180 * state.gyro.multiplier;
updateGyroView();
}
});
function updateGyroView() {
// Apply 3D rotation to the World Div
// rotateX for Beta (X axis tilt), rotateY for Gamma (Y axis pan)
world.style.transform = `rotateX(${-state.gyro.currentX}deg) rotateY(${state.gyro.currentY}deg)`;
hudX.innerText = Math.round(state.gyro.currentX);
hudY.innerText = Math.round(state.gyro.currentY);
}
// Permission Request
document.getElementById('reqPermission').addEventListener('click', () => {
if (typeof DeviceOrientationEvent !== 'undefined' && typeof DeviceOrientationEvent.requestPermission === 'function') {
DeviceOrientationEvent.requestPermission()
.then(response => {
if (response === 'granted') {
window.addEventListener('deviceorientation', handleOrientation);
document.getElementById('reqPermission').style.display = 'none';
}
})
.catch(console.error);
} else {
window.addEventListener('deviceorientation', handleOrientation);
document.getElementById('reqPermission').style.display = 'none';
}
});
// Start Loop
requestAnimationFrame(animate);
</script>
</body>
</html>