Skip to content
LAM
Read Home Blog
Make Projects HTML Tools Games
Touch grass Notes Resume Links
Home Blog HTML Projects
Tools Games Notes Resume Links
Back Interactive ASCII Art Grid Programming
Download Open
Show description 175 chars · Programming

Interactive ASCII Art Grid

Interactive ASCII Art Grid









Interactive ASCII Art Grid

Chat with the AI and see its visual response on the grid.





















Send

Interactive ASCII Art Grid

12,516 bytes · HTML source
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Interactive ASCII Art Grid</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <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=Roboto+Mono:wght@400;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Roboto Mono', monospace;
        }
        /* Custom styles for the loader */
        .loader {
            border: 4px solid #f3f3f3;
            border-top: 4px solid #3498db;
            border-radius: 50%;
            width: 40px;
            height: 40px;
            animation: spin 1s linear infinite;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        /* Ensure the pre tag maintains its structure */
        pre {
            white-space: pre;
            overflow-x: auto;
        }
    </style>
</head>
<body class="bg-gray-900 text-gray-200 flex flex-col items-center justify-center min-h-screen p-4">

    <div class="w-full max-w-4xl mx-auto">
        <h1 class="text-3xl md:text-4xl font-bold text-center text-cyan-400 mb-2">Interactive ASCII Art Grid</h1>
        <p class="text-center text-gray-400 mb-4">Chat with the AI and see its visual response on the grid.</p>

        <!-- The Grid Display -->
        <div class="bg-gray-800 border-2 border-cyan-700 rounded-lg p-2 shadow-lg mb-4">
            <pre id="ascii-grid" class="text-center text-lg leading-tight text-green-400"></pre>
        </div>
        
        <!-- AI Response and Loader -->
        <div class="flex items-center justify-center space-x-4 h-12 mb-4">
            <div id="loader" class="loader hidden"></div>
            <p id="ai-response" class="text-lg text-yellow-300 text-center"></p>
        </div>

        <!-- Input Form -->
        <form id="prompt-form" class="flex flex-col sm:flex-row gap-2">
            <input 
                type="text" 
                id="prompt-input" 
                placeholder="Ask the AI to draw something... (e.g., 'draw a house')"
                class="flex-grow bg-gray-700 text-white border border-gray-600 rounded-lg px-4 py-3 focus:outline-none focus:ring-2 focus:ring-cyan-500"
                required>
            <button 
                type="submit" 
                id="send-button"
                class="bg-cyan-600 hover:bg-cyan-700 text-white font-bold py-3 px-6 rounded-lg transition-colors duration-300 disabled:bg-gray-500 disabled:cursor-not-allowed">
                Send
            </button>
        </form>
    </div>

    <script>
        // --- CONFIGURATION ---
        const GRID_COLS = 50;
        const GRID_ROWS = 20;
        const CHAR_SET = ['#', '@', '*', '.', ' ']; // The 5 characters the AI can use
        const ANIMATION_SPEED_MS = 150; // Speed for animations in milliseconds

        // --- DOM ELEMENTS ---
        const gridElement = document.getElementById('ascii-grid');
        const promptForm = document.getElementById('prompt-form');
        const promptInput = document.getElementById('prompt-input');
        const sendButton = document.getElementById('send-button');
        const loader = document.getElementById('loader');
        const aiResponseElement = document.getElementById('ai-response');

        // --- STATE ---
        let animationInterval = null;

        // --- CORE FUNCTIONS ---

        /**
         * Initializes the grid with empty spaces and renders it.
         */
        function initializeGrid() {
            const initialGridData = Array(GRID_ROWS).fill().map(() => Array(GRID_COLS).fill(' '));
            const welcomeMessage = "Hello! What should I draw for you?".split('');
            const startCol = Math.floor((GRID_COLS - welcomeMessage.length) / 2);
            const midRow = Math.floor(GRID_ROWS / 2);
            
            welcomeMessage.forEach((char, index) => {
                if(startCol + index < GRID_COLS) {
                    initialGridData[midRow][startCol + index] = char;
                }
            });
            renderGrid(initialGridData);
        }

        /**
         * Renders a 2D array of characters to the <pre> element.
         * @param {string[][]} gridData - The 2D array representing the grid.
         */
        function renderGrid(gridData) {
            if (!gridData || gridData.length === 0) {
                console.error("Invalid grid data provided to renderGrid.");
                return;
            }
            const gridString = gridData.map(row => row.join('')).join('\n');
            gridElement.textContent = gridString;
        }

        /**
         * Plays an animation by cycling through frames.
         * @param {string[][][]} frames - An array of grid data, where each is a frame.
         */
        function playAnimation(frames) {
            if (animationInterval) {
                clearInterval(animationInterval);
            }
            let currentFrame = 0;
            animationInterval = setInterval(() => {
                renderGrid(frames[currentFrame]);
                currentFrame = (currentFrame + 1) % frames.length;
            }, ANIMATION_SPEED_MS);
        }

        /**
         * Clears any ongoing animation.
         */
        function stopAnimation() {
            if (animationInterval) {
                clearInterval(animationInterval);
                animationInterval = null;
            }
        }

        /**
         * Calls the Gemini API with a structured prompt.
         * @param {string} userPrompt - The text from the user.
         */
        async function callGeminiAPI(userPrompt) {
            // This is the system prompt that tells the AI its role and capabilities.
            const systemPrompt = `You are GridAI, a creative AI that responds to prompts by generating ASCII art.
            
            Your capabilities:
            1. You can create static images.
            2. You can create simple animations (e.g., blinking, pulsing).
            3. You can animate the movement of images across the grid.
            
            Constraints:
            - Grid dimensions: ${GRID_COLS} columns by ${GRID_ROWS} rows.
            - Character set: [${CHAR_SET.map(c => `'${c}'`).join(', ')}]
            - Your output MUST be a valid JSON object following the specified schema.
            - For animations, provide an array of grids as frames. Keep animations short (2-8 frames).
            - For static images, the "animation" array should be empty, and the "grid" field should contain the image.
            - To show movement, create multiple frames where the object's position is different in each frame.
            - Always provide a brief, friendly text description of what you've created.
            - Be creative! Try to make interesting patterns and shapes.
            - If you cannot fulfill a request, explain why in the description and return an empty grid.
            
            Example of a static image request: "draw a smiley face"
            Example of a simple animation request: "make a blinking star"
            Example of a moving animation: "make a ball bounce from left to right"
            `;

            // Define the JSON schema for the AI's response
            const schema = {
                type: "OBJECT",
                properties: {
                    "description": { "type": "STRING" },
                    "grid": {
                        "type": "ARRAY",
                        "items": {
                            "type": "ARRAY",
                            "items": { "type": "STRING" }
                        }
                    },
                    "animation": {
                        "type": "ARRAY",
                        "items": {
                            "type": "ARRAY",
                            "items": {
                                "type": "ARRAY",
                                "items": { "type": "STRING" }
                            }
                        }
                    }
                },
                required: ["description", "grid", "animation"]
            };

            const payload = {
                systemInstruction: {
                    parts: [{ text: systemPrompt }]
                },
                contents: [
                    { role: "user", parts: [{ text: userPrompt }] }
                ],
                generationConfig: {
                    responseMimeType: "application/json",
                    responseSchema: schema
                }
            };
            
            const apiKey = ""; // API key is handled by the environment
            const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${apiKey}`;

            try {
                const response = await fetch(apiUrl, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(payload)
                });

                if (!response.ok) {
                    const errorBody = await response.text();
                    console.error("API Error Body:", errorBody);
                    throw new Error(`API Error: ${response.status} ${response.statusText}`);
                }

                const result = await response.json();
                
                if (result.candidates && result.candidates.length > 0) {
                    const text = result.candidates[0].content.parts[0].text;
                    return JSON.parse(text);
                } else {
                    throw new Error("Invalid response structure from API.");
                }

            } catch (error) {
                console.error("Error calling Gemini API:", error);
                // Return a structured error response to be displayed on the grid
                const errorGrid = Array(GRID_ROWS).fill().map(() => Array(GRID_COLS).fill(' '));
                const errorMessage = "Error: Could not generate art.".split('');
                 const startCol = Math.floor((GRID_COLS - errorMessage.length) / 2);
                errorMessage.forEach((char, i) => errorGrid[Math.floor(GRID_ROWS/2)][startCol+i] = char);

                return {
                    description: "Sorry, I encountered an error. Please try again.",
                    grid: errorGrid,
                    animation: []
                };
            }
        }

        /**
         * Handles the form submission event.
         */
        async function handleFormSubmit(event) {
            event.preventDefault();
            const prompt = promptInput.value.trim();
            if (!prompt) return;

            // --- UI updates for loading state ---
            stopAnimation();
            sendButton.disabled = true;
            loader.classList.remove('hidden');
            aiResponseElement.textContent = 'Generating...';
            promptInput.value = '';
            renderGrid(Array(GRID_ROWS).fill().map(() => Array(GRID_COLS).fill(' '))); // Clear grid

            // --- API Call and Rendering ---
            const aiArt = await callGeminiAPI(prompt);
            
            loader.classList.add('hidden');
            sendButton.disabled = false;

            if (aiArt) {
                aiResponseElement.textContent = aiArt.description;
                
                if (aiArt.animation && aiArt.animation.length > 0) {
                    playAnimation(aiArt.animation);
                } else if (aiArt.grid && aiArt.grid.length > 0) {
                    renderGrid(aiArt.grid);
                }
            } else {
                 aiResponseElement.textContent = "Something went wrong. Couldn't get a response.";
            }
        }


        // --- EVENT LISTENERS ---
        document.addEventListener('DOMContentLoaded', initializeGrid);
        promptForm.addEventListener('submit', handleFormSubmit);

    </script>
</body>
</html>