Created
August 25, 2025 20:17
-
-
Save amanat361/d10b7569fcc8c2b5695a6efd7bb677b8 to your computer and use it in GitHub Desktop.
3D cube for LLM streams
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, { useState, useEffect, useRef } from ‘react’; | |
| const TextCube = () => { | |
| const [faceTexts, setFaceTexts] = useState([’’, ‘’, ‘’, ‘’, ‘’, ‘’]); | |
| const [prompt, setPrompt] = useState(’’); | |
| const [isGenerating, setIsGenerating] = useState(false); | |
| const streamingIntervals = useRef([]); | |
| // Maximum characters to display per face | |
| const maxChars = 140; | |
| // Complete website project files | |
| const generateCodeFiles = () => { | |
| const htmlFile = `<!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Interactive Portfolio</title> | |
| <link rel="stylesheet" href="styles.css"> | |
| </head> | |
| <body> | |
| <nav class="navbar"> | |
| <div class="logo"> | |
| <span><dev/></span> | |
| </div> | |
| <ul class="nav-menu"> | |
| <li><a href="#home">Home</a></li> | |
| <li><a href="#projects">Projects</a></li> | |
| <li><a href="#contact">Contact</a></li> | |
| </ul> | |
| </nav> | |
| ``` | |
| <section class="hero"> | |
| <canvas id="particles"></canvas> | |
| <div class="hero-content"> | |
| <h1 class="glitch" data-text="Hello World">Hello World</h1> | |
| <p class="typing">I'm a <span id="role"></span></p> | |
| <button class="cta">View My Work</button> | |
| </div> | |
| </section> | |
| <section id="projects" class="projects"> | |
| <h2>Featured Projects</h2> | |
| <div class="project-grid"> | |
| <div class="project-card"> | |
| <img src="project1.jpg" alt="Project"> | |
| <h3>E-commerce Platform</h3> | |
| <p>Full-stack React application</p> | |
| </div> | |
| </div> | |
| </section> | |
| <script src="main.js"></script> | |
| ``` | |
| </body> | |
| </html>`; | |
| ``` | |
| const cssFile = `* { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| ``` | |
| } | |
| :root { | |
| –primary: #00ffff; | |
| –secondary: #ff00ff; | |
| –dark: #0a0a0a; | |
| –text: #ffffff; | |
| } | |
| body { | |
| font-family: ‘Courier New’, monospace; | |
| background: var(–dark); | |
| color: var(–text); | |
| overflow-x: hidden; | |
| } | |
| .navbar { | |
| position: fixed; | |
| width: 100%; | |
| padding: 1rem 2rem; | |
| background: rgba(10, 10, 10, 0.9); | |
| backdrop-filter: blur(10px); | |
| z-index: 1000; | |
| } | |
| .logo { | |
| font-size: 1.5rem; | |
| color: var(–primary); | |
| font-weight: bold; | |
| } | |
| .nav-menu { | |
| display: flex; | |
| list-style: none; | |
| gap: 2rem; | |
| float: right; | |
| } | |
| .nav-menu a { | |
| color: var(–text); | |
| text-decoration: none; | |
| transition: color 0.3s; | |
| } | |
| .nav-menu a:hover { | |
| color: var(–primary); | |
| } | |
| .hero { | |
| height: 100vh; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| position: relative; | |
| } | |
| #particles { | |
| position: absolute; | |
| width: 100%; | |
| height: 100%; | |
| top: 0; | |
| left: 0; | |
| } | |
| .hero-content { | |
| text-align: center; | |
| z-index: 10; | |
| } | |
| .glitch { | |
| font-size: 4rem; | |
| font-weight: bold; | |
| text-transform: uppercase; | |
| position: relative; | |
| color: var(–text); | |
| letter-spacing: 5px; | |
| animation: glitch 2s infinite; | |
| } | |
| .glitch::before, | |
| .glitch::after { | |
| content: attr(data-text); | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| } | |
| .glitch::before { | |
| animation: glitch-1 0.5s infinite; | |
| color: var(–primary); | |
| z-index: -1; | |
| } | |
| .glitch::after { | |
| animation: glitch-2 0.5s infinite; | |
| color: var(–secondary); | |
| z-index: -2; | |
| } | |
| @keyframes glitch { | |
| 0%, 100% { transform: translate(0); } | |
| 20% { transform: translate(-2px, 2px); } | |
| 40% { transform: translate(-2px, -2px); } | |
| 60% { transform: translate(2px, 2px); } | |
| 80% { transform: translate(2px, -2px); } | |
| } | |
| @keyframes glitch-1 { | |
| 0%, 100% { clip: rect(0, 900px, 0, 0); } | |
| 25% { clip: rect(0, 900px, 20px, 0); } | |
| 50% { clip: rect(40px, 900px, 60px, 0); } | |
| 75% { clip: rect(80px, 900px, 100px, 0); } | |
| } | |
| .cta { | |
| margin-top: 2rem; | |
| padding: 1rem 2rem; | |
| background: linear-gradient(45deg, var(–primary), var(–secondary)); | |
| border: none; | |
| color: var(–dark); | |
| font-weight: bold; | |
| cursor: pointer; | |
| transition: transform 0.3s; | |
| } | |
| .cta:hover { | |
| transform: scale(1.05); | |
| }`; | |
| ``` | |
| const jsFile = `// Particle System | |
| ``` | |
| class ParticleSystem { | |
| constructor(canvas) { | |
| this.canvas = canvas; | |
| this.ctx = canvas.getContext(‘2d’); | |
| this.particles = []; | |
| this.init(); | |
| } | |
| ``` | |
| init() { | |
| this.canvas.width = window.innerWidth; | |
| this.canvas.height = window.innerHeight; | |
| for (let i = 0; i < 100; i++) { | |
| this.particles.push({ | |
| x: Math.random() * this.canvas.width, | |
| y: Math.random() * this.canvas.height, | |
| vx: Math.random() * 2 - 1, | |
| vy: Math.random() * 2 - 1, | |
| size: Math.random() * 2 + 1 | |
| }); | |
| } | |
| this.animate(); | |
| } | |
| animate() { | |
| this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
| this.ctx.fillStyle = 'rgba(0, 255, 255, 0.5)'; | |
| this.particles.forEach(p => { | |
| p.x += p.vx; | |
| p.y += p.vy; | |
| if (p.x < 0 || p.x > this.canvas.width) p.vx *= -1; | |
| if (p.y < 0 || p.y > this.canvas.height) p.vy *= -1; | |
| this.ctx.beginPath(); | |
| this.ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); | |
| this.ctx.fill(); | |
| }); | |
| // Draw connections | |
| this.particles.forEach((p1, i) => { | |
| this.particles.slice(i + 1).forEach(p2 => { | |
| const dist = Math.hypot(p1.x - p2.x, p1.y - p2.y); | |
| if (dist < 100) { | |
| this.ctx.strokeStyle = \`rgba(0, 255, 255, \${0.2 * (1 - dist/100)})\`; | |
| this.ctx.beginPath(); | |
| this.ctx.moveTo(p1.x, p1.y); | |
| this.ctx.lineTo(p2.x, p2.y); | |
| this.ctx.stroke(); | |
| } | |
| }); | |
| }); | |
| requestAnimationFrame(() => this.animate()); | |
| } | |
| ``` | |
| } | |
| // Typing Effect | |
| class TypeWriter { | |
| constructor(element, words) { | |
| this.element = element; | |
| this.words = words; | |
| this.wordIndex = 0; | |
| this.charIndex = 0; | |
| this.isDeleting = false; | |
| this.type(); | |
| } | |
| ``` | |
| type() { | |
| const current = this.words[this.wordIndex % this.words.length]; | |
| if (this.isDeleting) { | |
| this.element.textContent = current.substring(0, this.charIndex--); | |
| } else { | |
| this.element.textContent = current.substring(0, this.charIndex++); | |
| } | |
| let speed = this.isDeleting ? 50 : 100; | |
| if (!this.isDeleting && this.charIndex === current.length) { | |
| speed = 2000; | |
| this.isDeleting = true; | |
| } else if (this.isDeleting && this.charIndex === 0) { | |
| this.isDeleting = false; | |
| this.wordIndex++; | |
| } | |
| setTimeout(() => this.type(), speed); | |
| } | |
| ``` | |
| } | |
| // Initialize when DOM loads | |
| document.addEventListener(‘DOMContentLoaded’, () => { | |
| const canvas = document.getElementById(‘particles’); | |
| if (canvas) new ParticleSystem(canvas); | |
| ``` | |
| const role = document.getElementById('role'); | |
| if (role) { | |
| new TypeWriter(role, ['Developer', 'Designer', 'Creator']); | |
| } | |
| // Smooth scroll | |
| document.querySelectorAll('a[href^="#"]').forEach(anchor => { | |
| anchor.addEventListener('click', e => { | |
| e.preventDefault(); | |
| const target = document.querySelector(anchor.getAttribute('href')); | |
| if (target) target.scrollIntoView({ behavior: 'smooth' }); | |
| }); | |
| }); | |
| ``` | |
| });`; | |
| ``` | |
| // Return 6 files: HTML, CSS, JS, then repeat for opposite faces | |
| return [htmlFile, cssFile, jsFile, htmlFile, cssFile, jsFile]; | |
| ``` | |
| }; | |
| const handleGenerate = () => { | |
| if (isGenerating) return; | |
| ``` | |
| setIsGenerating(true); | |
| setFaceTexts(['', '', '', '', '', '']); | |
| // Clear any existing intervals | |
| streamingIntervals.current.forEach(clearInterval); | |
| streamingIntervals.current = []; | |
| const codeFiles = generateCodeFiles(); | |
| // Stream each file character by character | |
| codeFiles.forEach((file, faceIndex) => { | |
| let charIndex = 0; | |
| const interval = setInterval(() => { | |
| if (charIndex < file.length) { | |
| setFaceTexts(prev => { | |
| const newTexts = [...prev]; | |
| newTexts[faceIndex] = (newTexts[faceIndex] + file[charIndex]).slice(-maxChars); | |
| return newTexts; | |
| }); | |
| charIndex++; | |
| } else { | |
| clearInterval(interval); | |
| streamingIntervals.current[faceIndex] = null; | |
| // Check if all faces are done streaming | |
| if (streamingIntervals.current.every(int => int === null)) { | |
| setIsGenerating(false); | |
| } | |
| } | |
| }, 20); // Fast streaming for code effect | |
| streamingIntervals.current[faceIndex] = interval; | |
| }); | |
| ``` | |
| }; | |
| // Cleanup intervals on unmount | |
| useEffect(() => { | |
| return () => { | |
| streamingIntervals.current.forEach(clearInterval); | |
| }; | |
| }, []); | |
| const faceStyles = { | |
| front: { | |
| transform: ‘translateZ(100px)’, | |
| color: ‘#0ff’, | |
| background: ‘rgba(0, 255, 255, 0.05)’, | |
| borderColor: ‘rgba(0, 255, 255, 0.3)’, | |
| }, | |
| right: { | |
| transform: ‘rotateY(90deg) translateZ(100px)’, | |
| color: ‘#f0f’, | |
| background: ‘rgba(255, 0, 255, 0.05)’, | |
| borderColor: ‘rgba(255, 0, 255, 0.3)’, | |
| }, | |
| top: { | |
| transform: ‘rotateX(90deg) translateZ(100px)’, | |
| color: ‘#ff0’, | |
| background: ‘rgba(255, 255, 0, 0.05)’, | |
| borderColor: ‘rgba(255, 255, 0, 0.3)’, | |
| }, | |
| back: { | |
| transform: ‘rotateY(180deg) translateZ(100px)’, | |
| color: ‘#0f0’, | |
| background: ‘rgba(0, 255, 0, 0.05)’, | |
| borderColor: ‘rgba(0, 255, 0, 0.3)’, | |
| }, | |
| left: { | |
| transform: ‘rotateY(-90deg) translateZ(100px)’, | |
| color: ‘#f80’, | |
| background: ‘rgba(255, 136, 0, 0.05)’, | |
| borderColor: ‘rgba(255, 136, 0, 0.3)’, | |
| }, | |
| bottom: { | |
| transform: ‘rotateX(-90deg) translateZ(100px)’, | |
| color: ‘#88f’, | |
| background: ‘rgba(136, 136, 255, 0.05)’, | |
| borderColor: ‘rgba(136, 136, 255, 0.3)’, | |
| }, | |
| }; | |
| const faceLabels = [‘index.html’, ‘styles.css’, ‘main.js’, ‘index.html’, ‘styles.css’, ‘main.js’]; | |
| const containerStyle = { | |
| width: ‘100vw’, | |
| height: ‘100vh’, | |
| display: ‘flex’, | |
| flexDirection: ‘column’, | |
| justifyContent: ‘center’, | |
| alignItems: ‘center’, | |
| background: ‘#1a1a1a’, | |
| perspective: ‘1000px’, | |
| margin: 0, | |
| padding: 0, | |
| gap: ‘40px’, | |
| }; | |
| const cubeStyle = { | |
| width: ‘200px’, | |
| height: ‘200px’, | |
| position: ‘relative’, | |
| transformStyle: ‘preserve-3d’, | |
| animation: ‘rotate 10s infinite linear’, | |
| }; | |
| const faceBaseStyle = { | |
| position: ‘absolute’, | |
| width: ‘200px’, | |
| height: ‘200px’, | |
| display: ‘flex’, | |
| flexDirection: ‘column’, | |
| fontSize: ‘11px’, | |
| lineHeight: ‘1.2’, | |
| overflow: ‘hidden’, | |
| wordBreak: ‘break-all’, | |
| padding: ‘10px’, | |
| boxSizing: ‘border-box’, | |
| border: ‘1px solid’, | |
| fontFamily: ‘monospace’, | |
| }; | |
| const labelStyle = { | |
| fontSize: ‘10px’, | |
| fontWeight: ‘bold’, | |
| marginBottom: ‘5px’, | |
| opacity: 0.8, | |
| borderBottom: ‘1px solid rgba(255, 255, 255, 0.1)’, | |
| paddingBottom: ‘3px’, | |
| }; | |
| const codeStyle = { | |
| flex: 1, | |
| overflow: ‘hidden’, | |
| whiteSpace: ‘pre-wrap’, | |
| wordWrap: ‘break-word’, | |
| margin: 0, | |
| }; | |
| const inputContainerStyle = { | |
| display: ‘flex’, | |
| gap: ‘10px’, | |
| padding: ‘20px’, | |
| background: ‘rgba(255, 255, 255, 0.05)’, | |
| borderRadius: ‘8px’, | |
| border: ‘1px solid rgba(255, 255, 255, 0.1)’, | |
| }; | |
| const inputStyle = { | |
| padding: ‘10px 15px’, | |
| fontSize: ‘16px’, | |
| background: ‘rgba(255, 255, 255, 0.1)’, | |
| border: ‘1px solid rgba(255, 255, 255, 0.2)’, | |
| borderRadius: ‘4px’, | |
| color: ‘#fff’, | |
| width: ‘300px’, | |
| fontFamily: ‘monospace’, | |
| }; | |
| const buttonStyle = { | |
| padding: ‘10px 20px’, | |
| fontSize: ‘16px’, | |
| background: isGenerating ? ‘rgba(100, 100, 100, 0.3)’ : ‘rgba(0, 255, 255, 0.2)’, | |
| border: ‘1px solid rgba(0, 255, 255, 0.5)’, | |
| borderRadius: ‘4px’, | |
| color: isGenerating ? ‘#666’ : ‘#0ff’, | |
| cursor: isGenerating ? ‘not-allowed’ : ‘pointer’, | |
| fontFamily: ‘monospace’, | |
| transition: ‘all 0.3s’, | |
| }; | |
| return ( | |
| <div style={containerStyle}> | |
| <style>{` | |
| @keyframes rotate { | |
| from { | |
| transform: rotateX(-20deg) rotateY(30deg); | |
| } | |
| to { | |
| transform: rotateX(-20deg) rotateY(390deg); | |
| } | |
| } | |
| ``` | |
| input::placeholder { | |
| color: rgba(255, 255, 255, 0.4); | |
| } | |
| button:hover:not(:disabled) { | |
| background: rgba(0, 255, 255, 0.3) !important; | |
| box-shadow: 0 0 20px rgba(0, 255, 255, 0.5); | |
| } | |
| `}</style> | |
| <div style={inputContainerStyle}> | |
| <input | |
| type="text" | |
| placeholder="e.g. 'Build me a portfolio website'" | |
| value={prompt} | |
| onChange={(e) => setPrompt(e.target.value)} | |
| onKeyPress={(e) => e.key === 'Enter' && handleGenerate()} | |
| style={inputStyle} | |
| disabled={isGenerating} | |
| /> | |
| <button | |
| onClick={handleGenerate} | |
| style={buttonStyle} | |
| disabled={isGenerating} | |
| > | |
| {isGenerating ? 'Generating...' : 'Generate Code'} | |
| </button> | |
| </div> | |
| <div style={cubeStyle}> | |
| {Object.entries(faceStyles).map(([face, style], index) => ( | |
| <div | |
| key={face} | |
| style={{ | |
| ...faceBaseStyle, | |
| ...style, | |
| borderColor: style.borderColor, | |
| }} | |
| > | |
| <div style={{...labelStyle, color: style.color}}> | |
| {faceLabels[index]} | |
| </div> | |
| <p style={{...codeStyle, color: style.color}}> | |
| {faceTexts[index]} | |
| </p> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| ``` | |
| ); | |
| }; | |
| export default TextCube; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment