Animetrix_AI / backend /static /index.html
SayedZahur786's picture
Add favicon support to frontend
dde9859
<!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>Animetrix AI | AI Animation Engine</title>
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<!-- Fonts: Inter (Modern, Clean) -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap"
rel="stylesheet"
/>
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Three.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<!-- Custom Config -->
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ["Inter", "sans-serif"],
},
colors: {
black: "#000000",
obsidian: "#0A0A0A",
orange: {
50: "#FFF7ED",
100: "#FFEDD5",
200: "#FED7AA",
300: "#FDBA74",
400: "#FB923C",
500: "#F97316",
600: "#EA580C",
700: "#C2410C",
800: "#9A3412",
900: "#7C2D12",
950: "#431407",
brand: "#FF6B00", // Vibrant Neon Orange
},
},
animation: {
glow: "glow 4s ease-in-out infinite",
float: "float 6s ease-in-out infinite",
},
keyframes: {
glow: {
"0%, 100%": { opacity: 0.3 },
"50%": { opacity: 0.6 },
},
float: {
"0%, 100%": { transform: "translateY(0)" },
"50%": { transform: "translateY(-10px)" },
},
},
},
},
};
</script>
<style>
body {
background-color: #000000;
color: #ffffff;
overflow-x: hidden;
width: 100vw;
min-height: 100vh;
}
#canvas-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
pointer-events: none;
}
.glass {
background: rgba(10, 10, 10, 0.7);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid rgba(255, 107, 0, 0.1);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.8);
}
.glass-hover:hover {
border-color: rgba(255, 107, 0, 0.3);
background: rgba(15, 15, 15, 0.8);
}
.orange-gradient-text {
background: linear-gradient(135deg, #ff6b00 0%, #ffa500 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.btn-orange {
background: linear-gradient(135deg, #ff6b00 0%, #ea580c 100%);
color: white;
font-weight: 600;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
overflow: hidden;
}
.btn-orange::after {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(255, 255, 255, 0.2),
transparent
);
transition: 0.5s;
}
.btn-orange:hover::after {
left: 100%;
}
.btn-orange:hover {
transform: translateY(-2px);
box-shadow: 0 10px 20px rgba(255, 107, 0, 0.3);
}
.input-dark {
background: rgba(0, 0, 0, 0.4);
border: 1px solid rgba(255, 255, 255, 0.05);
transition: all 0.3s ease;
}
.input-dark:focus {
border-color: #ff6b00;
background: rgba(0, 0, 0, 0.6);
outline: none;
box-shadow: 0 0 0 2px rgba(255, 107, 0, 0.1);
}
/* Custom Scrollbar */
::-webkit-scrollbar {
width: 4px;
}
::-webkit-scrollbar-track {
background: #000;
}
::-webkit-scrollbar-thumb {
background: #333;
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: #ff6b00;
}
.mesh-gradient {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: radial-gradient(
at 0% 0%,
rgba(255, 107, 0, 0.15) 0px,
transparent 50%
),
radial-gradient(
at 100% 100%,
rgba(255, 107, 0, 0.1) 0px,
transparent 50%
);
z-index: -1;
}
</style>
</head>
<body class="antialiased font-sans">
<div class="mesh-gradient"></div>
<div id="canvas-container"></div>
<!-- Header -->
<nav
class="fixed top-0 w-full z-50 border-b border-white/5 bg-black/50 backdrop-blur-xl"
>
<div
class="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between"
>
<div class="flex items-center gap-3 group cursor-pointer">
<div
class="w-8 h-8 rounded bg-orange-brand flex items-center justify-center transition-transform group-hover:rotate-12"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5 text-black"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
stroke-width="3"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M13 10V3L4 14h7v7l9-11h-7z"
/>
</svg>
</div>
<span class="font-extrabold text-xl tracking-tighter text-white"
>ANIMETRIX AI</span
>
</div>
<div class="flex items-center gap-6">
<div
class="hidden md:flex items-center gap-2 px-3 py-1 rounded-full bg-orange-950/30 border border-orange-900/50"
>
<span
class="w-2 h-2 rounded-full bg-orange-brand animate-pulse"
></span>
<span
class="text-[10px] font-bold text-orange-400 uppercase tracking-widest"
>Engine Online</span
>
</div>
</div>
</div>
</nav>
<main class="relative z-10 pt-24 pb-20 px-6 max-w-7xl mx-auto">
<div class="flex flex-col lg:flex-row gap-12">
<!-- Left: Controls -->
<div class="w-full lg:w-5/12 space-y-8">
<div class="space-y-4">
<h1
class="text-5xl md:text-6xl font-black tracking-tighter leading-[0.9]"
>
ANIMATE <br />
<span class="orange-gradient-text">IDEAS.</span>
</h1>
<p class="text-zinc-400 text-lg font-medium max-w-sm leading-snug">
The next generation of educational content. Powered by AI,
rendered with Manim.
</p>
</div>
<div class="glass rounded-3xl p-8 space-y-6">
<div class="space-y-2">
<label
class="text-[10px] font-black text-orange-brand uppercase tracking-[0.2em]"
>Prompt Input</label
>
<textarea
id="promptInput"
rows="4"
class="w-full input-dark rounded-2xl p-5 text-white placeholder-zinc-600 text-base font-medium resize-none"
placeholder="Describe the concept you want to visualize..."
></textarea>
</div>
<div class="space-y-4">
<div class="flex flex-wrap gap-2">
<button
onclick="setPrompt('Pythagorean theorem visualization')"
class="px-4 py-2 rounded-xl bg-zinc-900/50 border border-white/5 text-xs font-semibold text-zinc-400 hover:text-orange-400 hover:border-orange-900/50 transition-all"
>
📐 Geometry
</button>
<button
onclick="setPrompt('Binary search algorithm step by step')"
class="px-4 py-2 rounded-xl bg-zinc-900/50 border border-white/5 text-xs font-semibold text-zinc-400 hover:text-orange-400 hover:border-orange-900/50 transition-all"
>
💻 Algorithms
</button>
<button
onclick="setPrompt('Structure of an atom with orbiting electrons')"
class="px-4 py-2 rounded-xl bg-zinc-900/50 border border-white/5 text-xs font-semibold text-zinc-400 hover:text-orange-400 hover:border-orange-900/50 transition-all"
>
⚛️ Physics
</button>
</div>
<button
onclick="generate()"
id="generateBtn"
class="w-full btn-orange py-5 rounded-2xl text-sm uppercase tracking-[0.15em] flex items-center justify-center gap-3"
>
<span>Generate Animation</span>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-5 w-5"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fill-rule="evenodd"
d="M10.293 3.293a1 1 0 011.414 0l6 6a1 1 0 010 1.414l-6 6a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-4.293-4.293a1 1 0 010-1.414z"
clip-rule="evenodd"
/>
</svg>
</button>
</div>
</div>
<div class="grid grid-cols-3 gap-4">
<div class="glass p-4 rounded-2xl text-center">
<div class="text-xl font-black text-white">4K</div>
<div
class="text-[8px] text-zinc-500 uppercase font-bold tracking-widest"
>
Ready
</div>
</div>
<div class="glass p-4 rounded-2xl text-center">
<div class="text-xl font-black text-white">AI</div>
<div
class="text-[8px] text-zinc-500 uppercase font-bold tracking-widest"
>
Driven
</div>
</div>
<div class="glass p-4 rounded-2xl text-center">
<div class="text-xl font-black text-white">FAST</div>
<div
class="text-[8px] text-zinc-500 uppercase font-bold tracking-widest"
>
Render
</div>
</div>
</div>
</div>
<!-- Right: Preview -->
<div class="w-full lg:w-7/12">
<div
class="relative aspect-video rounded-[2rem] overflow-hidden glass border-2 border-white/5 group"
>
<!-- Standby -->
<div
id="standbyScreen"
class="absolute inset-0 flex flex-col items-center justify-center bg-black/40"
>
<div class="relative">
<div
class="absolute inset-0 bg-orange-brand blur-3xl opacity-10 animate-pulse"
></div>
<div
class="w-20 h-20 rounded-full border border-orange-brand/20 flex items-center justify-center animate-float"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-10 w-10 text-orange-brand"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1"
d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1"
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
</div>
</div>
<p
class="mt-6 text-zinc-500 font-bold text-xs uppercase tracking-[0.3em]"
>
System Idle
</p>
</div>
<!-- Video -->
<video
id="videoPlayer"
class="absolute inset-0 w-full h-full object-cover hidden"
controls
playsinline
>
<source id="videoSource" src="" type="video/mp4" />
</video>
<!-- Status Overlay -->
<div
id="statusSection"
class="hidden absolute inset-0 bg-black/90 backdrop-blur-2xl flex flex-col items-center justify-center p-12 z-20"
>
<div class="w-full max-w-sm space-y-8">
<div class="flex justify-center">
<div class="relative">
<div
class="absolute inset-0 bg-orange-brand blur-2xl opacity-20 animate-ping"
></div>
<div
class="w-16 h-16 rounded-2xl bg-orange-brand flex items-center justify-center shadow-2xl shadow-orange-brand/50"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8 text-black animate-spin"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="3"
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
/>
</svg>
</div>
</div>
</div>
<div class="text-center space-y-2">
<h3
class="text-2xl font-black text-white tracking-tight"
id="statusTitle"
>
Processing
</h3>
<p
class="text-orange-brand/60 text-xs font-bold uppercase tracking-widest"
id="statusMessage"
>
Initializing...
</p>
</div>
<div class="space-y-4">
<div
class="h-1 w-full bg-zinc-900 rounded-full overflow-hidden"
>
<div
id="progressFill"
class="h-full bg-orange-brand w-0 transition-all duration-500 shadow-[0_0_15px_rgba(255,107,0,0.5)]"
></div>
</div>
<div
class="flex justify-between text-[8px] font-black text-zinc-600 uppercase tracking-[0.2em]"
>
<span>Plan</span>
<span>Code</span>
<span>Render</span>
</div>
</div>
</div>
</div>
<!-- Error -->
<div
id="errorSection"
class="hidden absolute inset-0 bg-red-950/95 backdrop-blur-xl flex flex-col items-center justify-center p-12 text-center z-30"
>
<div
class="w-16 h-16 rounded-full bg-red-500/10 border border-red-500/20 flex items-center justify-center mb-6"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-8 w-8 text-red-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
/>
</svg>
</div>
<h3 class="text-xl font-black text-white mb-2">System Failure</h3>
<p
id="errorMessage"
class="text-red-400/60 text-xs font-mono bg-black/40 p-4 rounded-xl border border-red-500/10 max-w-md overflow-auto max-h-32"
>
Error details...
</p>
<button
onclick="resetUI()"
class="mt-8 px-8 py-3 bg-white text-black text-xs font-black uppercase tracking-widest rounded-xl hover:bg-zinc-200 transition-colors"
>
Reset Engine
</button>
</div>
</div>
</div>
</div>
</main>
<!-- Three.js Background -->
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true,
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document
.getElementById("canvas-container")
.appendChild(renderer.domElement);
// Particles
const particlesGeometry = new THREE.BufferGeometry();
const count = 1500;
const positions = new Float32Array(count * 3);
const colors = new Float32Array(count * 3);
for (let i = 0; i < count * 3; i++) {
positions[i] = (Math.random() - 0.5) * 15;
// Orange to Black gradient for particles
const mixedColor = new THREE.Color("#FF6B00").lerp(
new THREE.Color("#000000"),
Math.random() * 0.8
);
colors[i] = mixedColor.r;
colors[i + 1] = mixedColor.g;
colors[i + 2] = mixedColor.b;
}
particlesGeometry.setAttribute(
"position",
new THREE.BufferAttribute(positions, 3)
);
particlesGeometry.setAttribute(
"color",
new THREE.BufferAttribute(colors, 3)
);
const particlesMaterial = new THREE.PointsMaterial({
size: 0.015,
vertexColors: true,
transparent: true,
opacity: 0.4,
blending: THREE.AdditiveBlending,
});
const particles = new THREE.Points(particlesGeometry, particlesMaterial);
scene.add(particles);
camera.position.z = 5;
let mouseX = 0;
let mouseY = 0;
document.addEventListener("mousemove", (e) => {
mouseX = e.clientX / window.innerWidth - 0.5;
mouseY = e.clientY / window.innerHeight - 0.5;
});
function animate() {
requestAnimationFrame(animate);
particles.rotation.y += 0.001;
particles.rotation.x += 0.0005;
camera.position.x += (mouseX * 2 - camera.position.x) * 0.05;
camera.position.y += (-mouseY * 2 - camera.position.y) * 0.05;
camera.lookAt(scene.position);
renderer.render(scene, camera);
}
animate();
window.addEventListener("resize", () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
</script>
<!-- Logic -->
<script>
let statusCheckInterval = null;
function setPrompt(text) {
const input = document.getElementById("promptInput");
input.value = text;
input.focus();
}
function resetUI() {
document.getElementById("errorSection").classList.add("hidden");
document.getElementById("statusSection").classList.add("hidden");
document.getElementById("standbyScreen").classList.remove("hidden");
document.getElementById("videoPlayer").classList.add("hidden");
document.getElementById("videoPlayer").pause();
const btn = document.getElementById("generateBtn");
btn.disabled = false;
btn.querySelector("span").textContent = "Generate Animation";
}
async function generate() {
const prompt = document.getElementById("promptInput").value.trim();
if (!prompt) return;
document.getElementById("statusSection").classList.remove("hidden");
document.getElementById("errorSection").classList.add("hidden");
document.getElementById("standbyScreen").classList.add("hidden");
const btn = document.getElementById("generateBtn");
btn.disabled = true;
btn.querySelector("span").textContent = "Processing...";
try {
const response = await fetch("/generate", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ prompt: prompt }),
});
if (!response.ok) throw new Error("Network response was not ok");
startStatusPolling();
} catch (error) {
showError(error.message);
}
}
function startStatusPolling() {
if (statusCheckInterval) clearInterval(statusCheckInterval);
statusCheckInterval = setInterval(checkStatus, 1000);
}
async function checkStatus() {
try {
const response = await fetch("/status");
const status = await response.json();
updateUI(status);
if (status.stage === "success" || status.stage === "failed") {
clearInterval(statusCheckInterval);
}
} catch (error) {
console.error(error);
}
}
function updateUI(status) {
const titles = {
idle: "System Ready",
planning: "Strategizing",
coding: "Synthesizing Code",
executing: "Rendering Reality",
success: "Generation Complete",
failed: "System Error",
};
document.getElementById("statusTitle").textContent =
titles[status.stage] || "Processing";
document.getElementById("statusMessage").textContent = status.message;
const progressMap = {
idle: 0,
planning: 25,
coding: 50,
executing: 80,
success: 100,
failed: 100,
};
document.getElementById("progressFill").style.width =
(progressMap[status.stage] || 0) + "%";
if (status.stage === "success" && status.video_path) {
setTimeout(() => {
document.getElementById("statusSection").classList.add("hidden");
const video = document.getElementById("videoPlayer");
video.classList.remove("hidden");
document.getElementById("videoSource").src =
"/video/" + status.video_path;
video.load();
video.play();
const btn = document.getElementById("generateBtn");
btn.disabled = false;
btn.querySelector("span").textContent = "Generate New";
}, 800);
}
if (status.stage === "failed") {
showError(status.error || "Unknown error occurred");
}
}
function showError(msg) {
document.getElementById("statusSection").classList.add("hidden");
document.getElementById("errorSection").classList.remove("hidden");
document.getElementById("errorMessage").textContent = msg;
const btn = document.getElementById("generateBtn");
btn.disabled = false;
btn.querySelector("span").textContent = "Retry";
}
</script>
</body>
</html>