Something went wrong. Try again.
Monorepo for Aesthetic.Computer aesthetic.computer
Something went wrong. Try again.
8.7 kB ยท 258 lines
JavaScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259#!/usr/bin/env node
// Bouncing Ball Demo with Ultra-Fast Sixel Rendering// Targeting 30-60 FPS performance
import { performance } from 'perf_hooks';
class UltraFastSixelRenderer { constructor(width = 64, height = 64, scale = 3) { this.width = width; this.height = height; this.scale = scale; this.outputWidth = width * scale; this.outputHeight = height * scale; // Pre-allocate buffer this.buffer = new Uint8Array(width * height); // Pre-compute scaled positions for faster rendering this.scaleMap = new Array(this.outputWidth * this.outputHeight); for (let y = 0; y < this.outputHeight; y++) { for (let x = 0; x < this.outputWidth; x++) { const srcX = Math.floor(x / scale); const srcY = Math.floor(y / scale); this.scaleMap[y * this.outputWidth + x] = srcY * width + srcX; } } // Simple 2-color palette for maximum speed this.palette = [ '\x1b[48;2;0;0;0m', // Black background '\x1b[48;2;255;255;255m' // White ball ]; // Pre-build sixel color definitions this.sixelColors = '#0;2;0;0;0#1;2;100;100;100'; // Alternative screen buffer for fastest rendering process.stdout.write('\x1b[?1049h\x1b[H'); } clear() { this.buffer.fill(0); } // Ultra-fast circle drawing using integer math drawCircle(centerX, centerY, radius, color = 1) { const radiusSquared = radius * radius; const minX = Math.max(0, centerX - radius); const maxX = Math.min(this.width - 1, centerX + radius); const minY = Math.max(0, centerY - radius); const maxY = Math.min(this.height - 1, centerY + radius); for (let y = minY; y <= maxY; y++) { const dy = y - centerY; const dySquared = dy * dy; for (let x = minX; x <= maxX; x++) { const dx = x - centerX; const dxSquared = dx * dx; if (dxSquared + dySquared <= radiusSquared) { this.buffer[y * this.width + x] = color; } } } } // Ultra-fast sixel rendering using direct method render() { const bands = Math.ceil(this.outputHeight / 6); let sixel = `\x1b[H\x1bPq${this.sixelColors}`; for (let band = 0; band < bands; band++) { const startY = band * 6; // Process each color for (let colorIndex = 0; colorIndex < 2; colorIndex++) { if (colorIndex > 0) sixel += '$'; // Carriage return for new color sixel += `#${colorIndex}`; let hasPixels = false; let currentRun = ''; let runLength = 0; let lastChar = ''; for (let x = 0; x < this.outputWidth; x++) { let sixelChar = 0; // Check 6 pixels vertically for this band for (let bit = 0; bit < 6; bit++) { const y = startY + bit; if (y < this.outputHeight) { const srcIndex = this.scaleMap[y * this.outputWidth + x]; const pixelColor = this.buffer[srcIndex]; if (pixelColor === colorIndex) { sixelChar |= (1 << bit); } } } const char = String.fromCharCode(63 + sixelChar); if (char === lastChar && runLength < 99) { runLength++; } else { if (runLength > 3) { currentRun += `!${runLength}${lastChar}`; } else { currentRun += lastChar.repeat(runLength); } lastChar = char; runLength = 1; } if (sixelChar > 0) hasPixels = true; } // Add final run if (runLength > 3) { currentRun += `!${runLength}${lastChar}`; } else { currentRun += lastChar.repeat(runLength); } if (hasPixels) { sixel += currentRun; } currentRun = ''; runLength = 0; lastChar = ''; } if (band < bands - 1) sixel += '-'; // New line for next band } sixel += '\x1b\\'; // End sixel sequence process.stdout.write(sixel); } cleanup() { process.stdout.write('\x1b[?1049l'); // Exit alt screen }}
class BouncingBall { constructor(renderer) { this.renderer = renderer; this.x = renderer.width / 2; this.y = renderer.height / 2; this.vx = 0.8; this.vy = 0.6; this.radius = 4; // Boundary collision with radius consideration this.minX = this.radius; this.maxX = renderer.width - this.radius; this.minY = this.radius; this.maxY = renderer.height - this.radius; } update() { // Update position this.x += this.vx; this.y += this.vy; // Bounce off walls if (this.x <= this.minX || this.x >= this.maxX) { this.vx = -this.vx; this.x = Math.max(this.minX, Math.min(this.maxX, this.x)); } if (this.y <= this.minY || this.y >= this.maxY) { this.vy = -this.vy; this.y = Math.max(this.minY, Math.min(this.maxY, this.y)); } } draw() { this.renderer.drawCircle( Math.round(this.x), Math.round(this.y), this.radius ); }}
async function runBouncingBallDemo(duration = 10000) { const renderer = new UltraFastSixelRenderer(64, 64, 3); const ball = new BouncingBall(renderer); console.log('๐ Starting Bouncing Ball Demo'); console.log(`Target: 30-60 FPS for ${duration/1000} seconds`); console.log('Resolution: 64x64 @ 3x scale = 192x192 output'); console.log('Press Ctrl+C to exit\n'); const startTime = performance.now(); let frameCount = 0; let lastFpsTime = startTime; let lastFpsFrames = 0; const animate = () => { const currentTime = performance.now(); // Clear and update renderer.clear(); ball.update(); ball.draw(); renderer.render(); frameCount++; // Calculate FPS every 60 frames if (frameCount % 60 === 0) { const elapsed = currentTime - lastFpsTime; const fps = (frameCount - lastFpsFrames) / (elapsed / 1000); process.stdout.write(`\x1b[${renderer.outputHeight + 2};1H`); process.stdout.write(`\x1b[K๐ฏ FPS: ${fps.toFixed(1)} | Frame: ${frameCount} | Time: ${((currentTime - startTime)/1000).toFixed(1)}s`); lastFpsTime = currentTime; lastFpsFrames = frameCount; } // Continue animation if (currentTime - startTime < duration) { setImmediate(animate); } else { const totalTime = currentTime - startTime; const avgFps = frameCount / (totalTime / 1000); renderer.cleanup(); console.log(`\n\n๐ Demo Complete!`); console.log(`๐ Average FPS: ${avgFps.toFixed(1)}`); console.log(`๐ Total Frames: ${frameCount}`); console.log(`โฑ๏ธ Total Time: ${(totalTime/1000).toFixed(1)}s`); if (avgFps >= 30) { console.log(`โ
SUCCESS: Achieved target 30+ FPS!`); } else { console.log(`โ Below target: ${avgFps.toFixed(1)} FPS`); } } }; // Handle cleanup on exit process.on('SIGINT', () => { renderer.cleanup(); console.log('\n\n๐ Demo interrupted'); process.exit(0); }); animate();}
// CLI interfaceconst args = process.argv.slice(2);const duration = args[0] ? parseInt(args[0]) : 10000;
runBouncingBallDemo(duration);