🎮ArcadeLab

Lokales KI-Bildstudio – Maximale Erweiterung

by BlazeKoala99
674 lines24.5 KB
▶ Play
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lokales KI-Bildstudio – Maximale Erweiterung</title>
    <style>
        :root {
            --bg-color: #0b0f19;
            --card-bg: #151f32;
            --accent: #8b5cf6;
            --accent-hover: #7c3aed;
            --text-main: #f3f4f6;
            --text-muted: #9ca3af;
            --border: #374151;
        }

        body {
            font-family: 'Segoe UI', system-ui, sans-serif;
            background-color: var(--bg-color);
            color: var(--text-main);
            margin: 0;
            padding: 20px;
            display: flex;
            flex-direction: column;
            align-items: center;
            min-height: 100vh;
        }

        .container {
            max-width: 1000px;
            width: 100%;
            background: var(--card-bg);
            padding: 30px;
            border-radius: 16px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.5);
            box-sizing: border-box;
        }

        h1 {
            text-align: center;
            margin: 0 0 5px 0;
            font-size: 2.3rem;
            background: linear-gradient(to right, #a78bfa, #f472b6);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }

        .subtitle {
            text-align: center;
            color: var(--text-muted);
            margin-bottom: 30px;
            font-size: 0.95rem;
        }

        .grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 25px;
        }

        @media (max-width: 768px) {
            .grid { grid-template-columns: 1fr; }
        }

        .panel {
            background: #1e293b;
            border: 1px solid var(--border);
            padding: 20px;
            border-radius: 12px;
            display: flex;
            flex-direction: column;
            gap: 15px;
        }

        .panel h3 { margin-top: 0; color: #c084fc; }

        select, input[type="file"], input[type="text"] {
            width: 100%;
            padding: 10px;
            background: #0f172a;
            border: 1px solid var(--border);
            color: white;
            border-radius: 6px;
            box-sizing: border-box;
        }

        button {
            background: var(--accent);
            color: white;
            font-weight: bold;
            padding: 12px;
            border: none;
            border-radius: 6px;
            font-size: 15px;
            cursor: pointer;
            transition: background 0.2s;
        }

        button:hover { background: var(--accent-hover); }

        .preview-area {
            margin-top: 30px;
            background: #090d16;
            border: 2px dashed var(--border);
            border-radius: 12px;
            min-height: 400px;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            padding: 20px;
        }

        #canvasOutput {
            max-width: 100%;
            max-height: 500px;
            border-radius: 8px;
            box-shadow: 0 4px 15px rgba(0,0,0,0.6);
            display: none;
        }

        .status-text {
            color: #34d399;
            font-weight: bold;
            display: none;
            margin-top: 10px;
        }
    </style>
</head>
<body>

<div class="container">
    <h1>🔮 Lokales KI-Bildstudio Pro</h1>
    <div class="subtitle">Hochwertige Erstellungs-AI, Upload, Bildfilter & Erweiterte Hinzufüge-KI</div>

    <div class="grid">
        <div class="panel">
            <div style="border-bottom: 1px solid var(--border); padding-bottom: 15px;">
                <h3 style="color: #3b82f6;">🎨 Hochwertiger KI-Generator</h3>
                <label style="font-size: 0.9rem; color: var(--text-muted);">Was soll erstellt werden?</label>
                <input type="text" id="aiGenPrompt" placeholder="z.B. 'fußball logo', 'cyber-stadt', 'galaxie', 'magisches schloss'">
                <button onclick="generateWithMiniAI()" style="background: linear-gradient(to right, #3b82f6, #8b5cf6); margin-top: 10px;">High-End Bild generieren 🚀</button>
            </div>

            <div style="padding-top: 5px;">
                <h3 style="color: #10b981; font-size: 1.1rem;">📁 Eigenes Bild hochladen</h3>
                <input type="file" id="imageInput" accept="image/*" onchange="loadUserImage(event)">
            </div>
        </div>

        <div class="panel">
            <div style="border-bottom: 1px solid var(--border); padding-bottom: 15px;">
                <h3 style="color: #ec4899; font-size: 1.1rem;">🤖 Farb- & Effektfilter</h3>
                <input type="text" id="promptInput" placeholder="z.B. 'neongelb', 'dunkel', 'hell'">
                <button onclick="executeAIPrompt()" style="background: linear-gradient(to right, #8b5cf6, #ec4899); margin-top: 8px;">Filter anwenden ✨</button>
                <button onclick="removePixels()" style="background: linear-gradient(to right, #ef4444, #f59e0b); margin-top: 8px;">Schrift extrem schärfen 🔠</button>
            </div>

            <div>
                <h3 style="color: #fbbf24; font-size: 1.1rem;">➕ Erweiterte Hinzufüge-KI</h3>
                <label style="font-size: 0.9rem; color: var(--text-muted);">Objekt hinzufügen:</label>
                <input type="text" id="addPromptInput" placeholder="z.B. 'feuer', 'krone', 'flügel', 'portal', 'laser'">
                <button onclick="executeAddAI()" style="background: linear-gradient(to right, #f59e0b, #ef4444); margin-top: 8px;">Objekt einfügen 🌟</button>
                <div style="font-size: 0.75rem; color: var(--text-muted); margin-top: 5px;">
                    <b>Befehle:</b> feuer, krone, flügel, portal, laser, stern, schloss, herz
                </div>
            </div>
        </div>
    </div>

    <div class="preview-area">
        <div id="placeholderText" style="color: var(--text-muted);">Wählen Sie links eine Aktion aus...</div>
        <canvas id="canvasOutput"></canvas>
        <p id="statusText" class="status-text"></p>
        <button id="downloadBtn" style="display: none; margin-top: 15px; background: #10b981;" onclick="downloadCanvas()">💾 Bild lokal speichern</button>
    </div>
</div>

<script>
    const canvas = document.getElementById('canvasOutput');
    const ctx = canvas.getContext('2d');
    const placeholderText = document.getElementById('placeholderText');
    const statusText = document.getElementById('statusText');
    const downloadBtn = document.getElementById('downloadBtn');
    let originalBackup = null;

    function showStatus(text, delay = 0) {
        statusText.innerText = text;
        statusText.style.display = 'block';
        if(delay > 0) setTimeout(() => { statusText.style.display = 'none'; }, delay);
    }

    // --- HOCHWERTIGE HIGH-END BILDERZEUGUNG ---
    function generateWithMiniAI() {
        const text = document.getElementById('aiGenPrompt').value.toLowerCase().trim();
        if (!text) {
            alert("Bitte gib einen Text für den Generator ein!");
            return;
        }

        placeholderText.style.display = 'none';
        canvas.style.display = 'block';
        downloadBtn.style.display = 'block';
        canvas.width = 800;
        canvas.height = 800;

        showStatus("KI generiert hochauflösendes Bild...");

        if (text.includes("fußball") || text.includes("logo") || text.includes("wappen") || text.includes("schild")) {
            let bgGrad = ctx.createRadialGradient(400, 400, 40, 400, 400, 520);
            bgGrad.addColorStop(0, "#1e293b");
            bgGrad.addColorStop(0.5, "#0f172a");
            bgGrad.addColorStop(1, "#020617");
            ctx.fillStyle = bgGrad;
            ctx.fillRect(0, 0, 800, 800);

            ctx.shadowColor = "rgba(0,0,0,0.8)";
            ctx.shadowBlur = 30;
            ctx.fillStyle = "#090d16";
            ctx.strokeStyle = "#0284c7";
            ctx.lineWidth = 12;
            ctx.beginPath();
            ctx.moveTo(400, 130);
            ctx.lineTo(640, 210);
            ctx.lineTo(640, 480);
            ctx.quadraticCurveTo(640, 700, 400, 740);
            ctx.quadraticCurveTo(160, 700, 160, 480);
            ctx.lineTo(160, 210);
            ctx.closePath();
            ctx.fill();
            ctx.stroke();
            ctx.shadowBlur = 0;

            ctx.strokeStyle = "#fbbf24";
            ctx.lineWidth = 5;
            ctx.beginPath();
            ctx.moveTo(400, 155);
            ctx.lineTo(615, 225);
            ctx.lineTo(615, 470);
            ctx.quadraticCurveTo(615, 665, 400, 705);
            ctx.quadraticCurveTo(185, 665, 185, 470);
            ctx.lineTo(185, 225);
            ctx.closePath();
            ctx.stroke();

            let ballGrd = ctx.createRadialGradient(385, 405, 10, 400, 420, 105);
            ballGrd.addColorStop(0, "#ffffff");
            ballGrd.addColorStop(0.7, "#cbd5e1");
            ballGrd.addColorStop(1, "#64748b");
            ctx.fillStyle = ballGrd;
            ctx.beginPath();
            ctx.arc(400, 420, 105, 0, Math.PI * 2);
            ctx.fill();
            ctx.strokeStyle = "#0f172a";
            ctx.lineWidth = 6;
            ctx.stroke();

            ctx.fillStyle = "#0f172a";
            ctx.beginPath();
            let rB = 38;
            for(let i=0; i<5; i++) {
                let angle = (i * 72 - 18) * Math.PI / 180;
                let bx = 400 + rB * Math.cos(angle);
                let by = 420 + rB * Math.sin(angle);
                if(i === 0) ctx.moveTo(bx, by);
                else ctx.lineTo(bx, by);
            }
            ctx.closePath();
            ctx.fill();

            const drawStar = (cx, cy, spikes, outerRadius, innerRadius) => {
                let rot = Math.PI / 2 * 3;
                let x = cx, y = cy;
                let step = Math.PI / spikes;
                ctx.beginPath();
                ctx.moveTo(cx, cy - outerRadius);
                for (let i = 0; i < spikes; i++) {
                    x = cx + Math.cos(rot) * outerRadius;
                    y = cy + Math.sin(rot) * outerRadius;
                    ctx.lineTo(x, y);
                    rot += step;
                    x = cx + Math.cos(rot) * innerRadius;
                    y = cy + Math.sin(rot) * innerRadius;
                    ctx.lineTo(x, y);
                    rot += step;
                }
                ctx.lineTo(cx, cy - outerRadius);
                ctx.closePath();
                ctx.fillStyle = "#fbbf24";
                ctx.shadowColor = "#f59e0b";
                ctx.shadowBlur = 10;
                ctx.fill();
                ctx.strokeStyle = "#d97706";
                ctx.lineWidth = 2;
                ctx.stroke();
                ctx.shadowBlur = 0;
            };
            drawStar(400, 220, 5, 28, 14);
            drawStar(330, 245, 5, 22, 11);
            drawStar(470, 245, 5, 22, 11);

            ctx.fillStyle = "#1e3a8a";
            ctx.strokeStyle = "#fbbf24";
            ctx.lineWidth = 4;
            ctx.beginPath();
            ctx.rect(240, 540, 320, 56);
            ctx.fill();
            ctx.stroke();

            ctx.fillStyle = "#ffffff";
            ctx.font = "bold 24px 'Segoe UI', sans-serif";
            ctx.textAlign = "center";
            ctx.fillText("CHAMPIONS FC", 400, 577);
        }
        else if (text.includes("galaxie") || text.includes("space") || text.includes("weltall")) {
            ctx.fillStyle = "#020617";
            ctx.fillRect(0, 0, 800, 800);

            for (let i = 0; i < 700; i++) {
                let x = Math.random() * 800;
                let y = Math.random() * 800;
                let radius = Math.random() * 2.5;
                let alpha = Math.random();
                ctx.fillStyle = Math.random() > 0.3 ? `rgba(255, 255, 255, ${alpha})` : `rgba(56, 189, 248, ${alpha})`;
                ctx.beginPath();
                ctx.arc(x, y, radius, 0, Math.PI * 2);
                ctx.fill();
            }

            let neb1 = ctx.createRadialGradient(350, 400, 30, 350, 400, 450);
            neb1.addColorStop(0, "rgba(168, 85, 247, 0.6)");
            neb1.addColorStop(0.5, "rgba(236, 72, 153, 0.3)");
            neb1.addColorStop(1, "rgba(0,0,0,0)");
            ctx.fillStyle = neb1;
            ctx.fillRect(0, 0, 800, 800);

            let neb2 = ctx.createRadialGradient(500, 450, 20, 500, 450, 380);
            neb2.addColorStop(0, "rgba(59, 130, 246, 0.5)");
            neb2.addColorStop(1, "rgba(0,0,0,0)");
            ctx.fillStyle = neb2;
            ctx.fillRect(0, 0, 800, 800);
        }
        else if (text.includes("schloss") || text.includes("magisch") || text.includes("burg")) {
            let sky = ctx.createLinearGradient(0, 0, 0, 800);
            sky.addColorStop(0, "#0f172a");
            sky.addColorStop(0.5, "#3b0764");
            sky.addColorStop(1, "#831843");
            ctx.fillStyle = sky;
            ctx.fillRect(0, 0, 800, 800);

            let moon = ctx.createRadialGradient(600, 200, 10, 600, 200, 90);
            moon.addColorStop(0, "#fef08a");
            moon.addColorStop(1, "rgba(234, 179, 8, 0)");
            ctx.fillStyle = moon;
            ctx.beginPath();
            ctx.arc(600, 200, 90, 0, Math.PI*2);
            ctx.fill();

            ctx.fillStyle = "#090d16";
            ctx.fillRect(250, 450, 300, 350);
            ctx.fillRect(200, 350, 80, 450);
            ctx.fillRect(520, 350, 80, 450);
            
            ctx.beginPath(); ctx.moveTo(190, 350); ctx.lineTo(240, 230); ctx.lineTo(290, 350); ctx.closePath(); ctx.fill();
            ctx.beginPath(); ctx.moveTo(510, 350); ctx.lineTo(560, 230); ctx.lineTo(610, 350); ctx.closePath(); ctx.fill();

            ctx.fillStyle = "#38bdf8";
            ctx.shadowColor = "#38bdf8";
            ctx.shadowBlur = 12;
            ctx.fillRect(230, 400, 20, 40);
            ctx.fillRect(550, 400, 20, 40);
            ctx.fillRect(380, 520, 40, 60);
            ctx.shadowBlur = 0;
        }
        else {
            ctx.fillStyle = "#090d16";
            ctx.fillRect(0, 0, 800, 800);

            let xPos = 0;
            while(xPos < 800) {
                let w = 50 + Math.random() * 80;
                let h = 300 + Math.random() * 450;
                
                ctx.fillStyle = "#111827";
                ctx.fillRect(xPos, 800 - h, w, h);

                let windowColor = Math.random() > 0.5 ? "#38bdf8" : "#ec4899";
                ctx.fillStyle = windowColor;
                for(let fx = xPos + 10; fx < xPos + w - 10; fx += 14) {
                    for(let fy = 800 - h + 20; fy < 770; fy += 20) {
                        if(Math.random() > 0.25) {
                            ctx.fillRect(fx, fy, 7, 10);
                        }
                    }
                }
                xPos += w - 6;
            }
        }

        originalBackup = ctx.getImageData(0, 0, canvas.width, canvas.height);
        showStatus("✨ High-End Bild erfolgreich erstellt!", 2500);
    }

    function loadUserImage(event) {
        const file = event.target.files[0];
        if (!file) return;

        const reader = new FileReader();
        reader.onload = function(e) {
            const img = new Image();
            img.onload = function() {
                placeholderText.style.display = 'none';
                canvas.style.display = 'block';
                downloadBtn.style.display = 'block';

                const maxDim = 800;
                let w = img.width;
                let h = img.height;
                if(w > maxDim || h > maxDim) {
                    if(w > h) { h = (maxDim/w)*h; w = maxDim; }
                    else { w = (maxDim/h)*w; h = maxDim; }
                }
                canvas.width = w;
                canvas.height = h;
                
                ctx.drawImage(img, 0, 0, w, h);
                originalBackup = ctx.getImageData(0, 0, w, h);
                showStatus("Bild erfolgreich geladen.", 2000);
            }
            img.src = e.target.result;
        }
        reader.readAsDataURL(file);
    }

    function executeAIPrompt() {
        if (!originalBackup) {
            alert("Bitte erstelle oder lade zuerst ein Bild!");
            return;
        }

        const promptText = document.getElementById('promptInput').value.toLowerCase().trim();
        if (!promptText) return;

        showStatus("Wende Filter an...");
        const w = originalBackup.width;
        const h = originalBackup.height;
        const src = originalBackup.data;
        const dst = ctx.createImageData(w, h);

        const isYellow = promptText.includes("gelb");
        const isPink = promptText.includes("pink");
        const isDark = promptText.includes("dunkel");
        const isBright = promptText.includes("hell");

        for (let i = 0; i < src.length; i += 4) {
            let r = src[i], g = src[i+1], b = src[i+2], a = src[i+3];
            let brightness = (r + g + b) / 3;

            if (brightness < 120) {
                if (isYellow) { r = 240; g = 255; b = 0; }
                else if (isPink) { r = 255; g = 20; b = 147; }
            }

            if (isDark) { r *= 0.6; g *= 0.6; b *= 0.6; }
            else if (isBright) { r = Math.min(255, r * 1.4); g = Math.min(255, g * 1.4); b = Math.min(255, b * 1.4); }

            dst.data[i] = r; dst.data[i+1] = g; dst.data[i+2] = b; dst.data[i+3] = a;
        }

        ctx.putImageData(dst, 0, 0);
        originalBackup = ctx.getImageData(0, 0, w, h);
        showStatus("✨ Filter erfolgreich!", 2000);
    }

    // --- AGGRESSIVER SCHÄRFUNGS- & KANTENFILTER FÜR VERPIXELTE SCHRIFT ---
    function removePixels() {
        if (!originalBackup) {
            alert("Bitte erstelle oder lade zuerst ein Bild!");
            return;
        }

        showStatus("Verstärke Schrift-Kontraste und Kanten...");
        const w = originalBackup.width;
        const h = originalBackup.height;
        const src = originalBackup.data;
        const dst = ctx.createImageData(w, h);
        const d = dst.data;

        // Ein starker Schärfungs-Kernel mit erhöhtem Mittelwert zur Beseitigung von Pixelblöcken
        const kernel = [
            -1, -1, -1,
            -1,  9, -1,
            -1, -1, -1
        ];

        for (let y = 1; y < h - 1; y++) {
            for (let x = 1; x < w - 1; x++) {
                let idx = (y * w + x) * 4;
                
                let r = 0, g = 0, b = 0;

                for (let ky = -1; ky <= 1; ky++) {
                    for (let kx = -1; kx <= 1; kx++) {
                        let nIdx = ((y + ky) * w + (x + kx)) * 4;
                        let weight = kernel[(ky + 1) * 3 + (kx + 1)];
                        
                        r += src[nIdx] * weight;
                        g += src[nIdx + 1] * weight;
                        b += src[nIdx + 2] * weight;
                    }
                }

                // Schwellenwert-Kontrastbooster: Helle Pixel noch heller, dunkle Pixel noch dunkler machen,
                // damit verpixelte Treppenstufen-Kanten bei Schrift wie richtige Linien wirken.
                r = r < 100 ? r * 0.8 : Math.min(255, r * 1.2);
                g = g < 100 ? g * 0.8 : Math.min(255, g * 1.2);
                b = b < 100 ? b * 0.8 : Math.min(255, b * 1.2);

                d[idx]     = Math.min(255, Math.max(0, r));
                d[idx + 1] = Math.min(255, Math.max(0, g));
                d[idx + 2] = Math.min(255, Math.max(0, b));
                d[idx + 3] = src[idx + 3];
            }
        }

        for (let x = 0; x < w; x++) {
            let topIdx = (0 * w + x) * 4;
            let botIdx = ((h - 1) * w + x) * 4;
            for(let c=0; c<4; c++) {
                d[topIdx + c] = src[topIdx + c];
                d[botIdx + c] = src[botIdx + c];
            }
        }

        ctx.putImageData(dst, 0, 0);
        originalBackup = ctx.getImageData(0, 0, w, h);
        showStatus("✨ Schrift-Kontraste maximiert!", 2500);
    }

    // --- ERWEITERTE MINI-HINZUFÜGE-KI ---
    function executeAddAI() {
        if (!originalBackup) {
            alert("Bitte erstelle oder lade zuerst ein Bild!");
            return;
        }

        const addText = document.getElementById('addPromptInput').value.toLowerCase().trim();
        if (!addText) {
            alert("Bitte gib ein Objekt ein (z.B. 'feuer', 'krone', 'portal', 'laser', 'flügel')!");
            return;
        }

        showStatus("Füge Objekt hinzu...");
        ctx.putImageData(originalBackup, 0, 0);

        const cx = canvas.width / 2;
        const cy = canvas.height / 2;

        if (addText.includes("feuer") || addText.includes("flamme")) {
            let flameGrd = ctx.createRadialGradient(cx, cy + 50, 10, cx, cy, 120);
            flameGrd.addColorStop(0, "#fef08a");
            flameGrd.addColorStop(0.4, "#f97316");
            flameGrd.addColorStop(0.8, "#ef4444");
            flameGrd.addColorStop(1, "rgba(0,0,0,0)");
            ctx.fillStyle = flameGrd;
            ctx.beginPath();
            ctx.arc(cx, cy, 120, 0, Math.PI * 2);
            ctx.fill();
        }
        else if (addText.includes("portal") || addText.includes("magie")) {
            let portal = ctx.createRadialGradient(cx, cy, 20, cx, cy, 150);
            portal.addColorStop(0, "#ffffff");
            portal.addColorStop(0.4, "#38bdf8");
            portal.addColorStop(0.8, "#8b5cf6");
            portal.addColorStop(1, "rgba(0,0,0,0)");
            ctx.fillStyle = portal;
            ctx.beginPath();
            ctx.arc(cx, cy, 150, 0, Math.PI * 2);
            ctx.fill();
        }
        else if (addText.includes("flügel") || addText.includes("wings")) {
            ctx.fillStyle = "rgba(56, 189, 248, 0.8)";
            ctx.shadowColor = "#38bdf8";
            ctx.shadowBlur = 15;
            ctx.beginPath();
            ctx.moveTo(cx - 50, cy);
            ctx.lineTo(cx - 200, cy - 120);
            ctx.lineTo(cx - 150, cy + 50);
            ctx.closePath();
            ctx.fill();

            ctx.beginPath();
            ctx.moveTo(cx + 50, cy);
            ctx.lineTo(cx + 200, cy - 120);
            ctx.lineTo(cx + 150, cy + 50);
            ctx.closePath();
            ctx.fill();
            ctx.shadowBlur = 0;
        }
        else if (addText.includes("laser") || addText.includes("strahl")) {
            ctx.strokeStyle = "#ef4444";
            ctx.lineWidth = 8;
            ctx.shadowColor = "#ef4444";
            ctx.shadowBlur = 20;
            ctx.beginPath();
            ctx.moveTo(0, cy - 50);
            ctx.lineTo(canvas.width, cy + 100);
            ctx.stroke();
            ctx.shadowBlur = 0;
        }
        else if (addText.includes("krone")) {
            ctx.fillStyle = "#fbbf24";
            ctx.strokeStyle = "#b45309";
            ctx.lineWidth = 3;
            ctx.beginPath();
            ctx.moveTo(cx - 70, cy - 80);
            ctx.lineTo(cx - 90, cy - 140);
            ctx.lineTo(cx - 30, cy - 100);
            ctx.lineTo(cx, cy - 150);
            ctx.lineTo(cx + 30, cy - 100);
            ctx.lineTo(cx + 90, cy - 140);
            ctx.lineTo(cx + 70, cy - 80);
            ctx.closePath();
            ctx.fill();
            ctx.stroke();
        }
        else if (addText.includes("stern")) {
            ctx.fillStyle = "#fbbf24";
            ctx.shadowColor = "#f59e0b";
            ctx.shadowBlur = 20;
            ctx.beginPath();
            let spikes = 5, outerRadius = 60, innerRadius = 25;
            let rot = Math.PI / 2 * 3;
            let x = cx, y = cy;
            let step = Math.PI / spikes;
            ctx.moveTo(x, y - outerRadius);
            for (let i = 0; i < spikes; i++) {
                ctx.lineTo(x + Math.cos(rot) * outerRadius, y + Math.sin(rot) * outerRadius);
                rot += step;
                ctx.lineTo(x + Math.cos(rot) * innerRadius, y + Math.sin(rot) * innerRadius);
                rot += step;
            }
            ctx.closePath();
            ctx.fill();
            ctx.shadowBlur = 0;
        }
        else {
            ctx.fillStyle = "#ec4899";
            ctx.shadowColor = "#ec4899";
            ctx.shadowBlur = 15;
            ctx.beginPath();
            ctx.arc(cx - 25, cy - 25, 35, Math.PI, 0, false);
            ctx.arc(cx + 25, cy - 25, 35, Math.PI, 0, false);
            ctx.lineTo(cx, cy + 50);
            ctx.closePath();
            ctx.fill();
            ctx.shadowBlur = 0;
        }

        originalBackup = ctx.getImageData(0, 0, canvas.width, canvas.height);
        showStatus("✨ Objekt erfolgreich hinzugefügt!", 2500);
    }

    function downloadCanvas() {
        const link = document.createElement('a');
        link.href = canvas.toDataURL('image/png');
        link.download = 'ki-studio-pro.png';
        link.click();
    }
</script>

</body>
</html>

Game Source: Lokales KI-Bildstudio – Maximale Erweiterung

Creator: BlazeKoala99

Libraries: none

Complexity: complex (674 lines, 24.5 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: lokales-ki-bildstudio-100-offline-blazekoala99" to link back to the original. Then publish at arcadelab.ai/publish.