Commit History

Start roles and theme blank
16301e6
Running
verified

LeafCat79 commited on

Add safe HF token validity check
32aa7c0
verified

LeafCat79 commited on

Start with empty paste-friendly HTML input
27c8f1d
verified

LeafCat79 commited on

Clean up forest background artifacts
cad6c7b
verified

LeafCat79 commited on

Keep backgrounds separate and prevent player trails
181fa20
verified

LeafCat79 commited on

Prevent missing images from black-screening canvas games
d47fc42
verified

LeafCat79 commited on

Use whole 2D sprite generator for sprite roles
d0928eb
verified

LeafCat79 commited on

Prevent sprite generations from becoming texture maps
162e683
verified

LeafCat79 commited on

Force free diffusion model to float32 on CPU
f7be081
verified

LeafCat79 commited on

Add free local diffusion dependencies
c4a0bf1
verified

LeafCat79 commited on

Use free local Diffusers image model
9998a8b
verified

LeafCat79 commited on

Add huggingface_hub dependency
b9bc5e1
verified

LeafCat79 commited on

Use HF Inference Providers
e721410
verified

LeafCat79 commited on

Show Hugging Face model errors
a3281e2
verified

LeafCat79 commited on

Report actual model sources
9380874
verified

LeafCat79 commited on

Document AI model pipeline
a908e86
verified

LeafCat79 commited on

Use AI prompt and image models
eed87b4
verified

LeafCat79 commited on

Add shape-aware sprite families
e2b36e5
verified

LeafCat79 commited on

Document image generation options
311fb99
verified

LeafCat79 commited on

Improve style-aware asset generation
567019b
verified

LeafCat79 commited on

Make projectiles vary with theme
2897473
verified

LeafCat79 commited on

Make sprites change with theme
b7eb313
verified

LeafCat79 commited on

Fix prompt refresh and broader asset applying
3d8179d
verified

LeafCat79 commited on

Improve top-down shooter asset matching
6b851d5
verified

LeafCat79 commited on

Improve background syncing and themes
752e159
verified

LeafCat79 commited on

Remove unused requests dependency
5a5bb7b
verified

LeafCat79 commited on

Use local fresh asset generator
23169ba
verified

LeafCat79 commited on

Generate fresh images on every run
1ec56bf
verified

LeafCat79 commited on

Fix gallery image outputs
30dc2ff
verified

LeafCat79 commited on

Remove accidental pasted-code folder
bc1d515
verified

LeafCat79 commited on

Update README.md
0c8ccf5
verified

LeafCat79 commited on

Update requirements.txt
b61de4a
verified

LeafCat79 commited on

Update README.md
3ffc37d
verified

LeafCat79 commited on

Create README.md
d7e3123
verified

LeafCat79 commited on

Create requirements.txt
0c7c679
verified

LeafCat79 commited on

Create app.py
4d60f00
verified

LeafCat79 commited on

Create import base64import ioimport reimport timefrom dataclasses import dataclassfrom html import escapefrom urllib.parse import quoteimport gradio as grimport requestsfrom PIL import ImagePOLLINATIONS_URL = ( "https://image.pollinations.ai/prompt/{prompt}" "?width={width}&height={height}&model=flux&nologo=true&seed={seed}")STARTER_HTML = """<!DOCTYPE html><html><head> <meta charset="UTF-8" /> <title>Mini Forest Game</title> <style> body { margin: 0; background: #111; display: grid; place-items: center; min-height: 100vh; } canvas { border: 2px solid #222; background: #222; image-rendering: pixelated; } </style></head><body> <canvas id="gameCanvas" width="800" height="450"></canvas> <script> const canvas = document.getElementById("gameCanvas"); const ctx = canvas.getContext("2d"); const background = new Image(); background.src = "sprite_background.png"; const playerImg = new Image(); playerImg.src = "sprite_player.png"; const keys = new Set(); const player = { x: 380, y: 205, w: 48, h: 48, speed: 4 }; window.addEventListener("keydown", e => keys.add(e.key)); window.addEventListener("keyup", e => keys.delete(e.key)); function update() { if (keys.has("a") || keys.has("ArrowLeft")) player.x -= player.speed; if (keys.has("d") || keys.has("ArrowRight")) player.x += player.speed; if (keys.has("w") || keys.has("ArrowUp")) player.y -= player.speed; if (keys.has("s") || keys.has("ArrowDown")) player.y += player.speed; player.x = Math.max(0, Math.min(canvas.width - player.w, player.x)); player.y = Math.max(0, Math.min(canvas.height - player.h, player.y)); } function draw() { ctx.drawImage(background, 0, 0, canvas.width, canvas.height); ctx.drawImage(playerImg, player.x, player.y, player.w, player.h); ctx.fillStyle = "white"; ctx.font = "18px sans-serif"; ctx.fillText("Use WASD or arrow keys", 18, 28); } function loop() { update(); draw(); requestAnimationFrame(loop); } loop(); </script></body></html>"""DEFAULT_ROLES = """player: top-down pixel-art adventurer hero, transparent background, bright readable silhouettebackground: enchanted forest clearing game background, top-down view, soft moonlight, detailed but not too busy"""@dataclassclass AssetSpec: role: str prompt: str filename: str width: int height: intdef slugify(value: str) -> str: value = re.sub(r"[^a-zA-Z0-9]+", "_", value.strip().lower()).strip("_") return value or "asset"def parse_assets(raw_roles: str, style_hint: str) -> list[AssetSpec]: specs: list[AssetSpec] = [] for line in raw_roles.splitlines(): line = line.strip() if not line or line.startswith("#"): continue if ":" in line: role, prompt = line.split(":", 1) elif "=" in line: role, prompt = line.split("=", 1) else: role, prompt = line, line role = role.strip() prompt = prompt.strip() or role slug = slugify(role) is_background = any(word in slug for word in ("background", "backdrop", "scene", "map", "level")) width, height = (800, 450) if is_background else (128, 128) filename = f"sprite_{slug}.png" full_prompt = ( f"{prompt}, {style_hint}, game asset, clean readable shape, " "no text, no watermark" ).strip(", ") specs.append(AssetSpec(role=role, prompt=full_prompt, filename=filename, width=width, height=height)) return specsdef image_to_data_uri(content: bytes, width: int, height: int) -> str: image = Image.open(io.BytesIO(content)).convert("RGBA") image = image.resize((width, height), Image.LANCZOS) out = io.BytesIO() image.save(out, format="PNG") return "data:image/png;base64," + base64.b64encode(out.getvalue()).decode("ascii")def placeholder_data_uri(role: str, width: int, height: int) -> str: label = escape(role[:18]) svg = f"""<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}"><rect width="100%" height="100%" fill="#222"/><rect x="4" y="4" width="{width - 8}" height="{height - 8}" rx="12" fill="#3b82f6"/><text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" fill="white" font-family="Arial" font-size="18">{label}</text></svg>""" return "data:image/svg+xml;base64," + base64.b64encode(svg.encode("utf-8")).decode("ascii")def generate_asset(spec: AssetSpec, index: int) -> tuple[str, str | None]: seed = abs(hash((spec.role, spec.prompt, index))) % 999999 url = POLLINATIONS_URL.format( prompt=quote(spec.prompt), width=spec.width, height=spec.height, seed=seed, ) for attempt in range(3): try: response = requests.get(url, timeout=120) response.raise_for_status() return image_to_data_uri(response.content, spec.width, spec.height), None except Exception as exc: if attempt < 2: time.sleep(8) else: return placeholder_data_uri(spec.role, spec.width, spec.height), str(exc) return placeholder_data_uri(spec.role, spec.width, spec.height), "Unknown generation error"def replacement_names(spec: AssetSpec) -> set[str]: slug = slugify(spec.role) names = { spec.filename, f"{slug}.png", f"{slug}.jpg", f"{slug}.jpeg", f"{slug}.webp", f"asset_{slug}.png", f"{spec.role.strip()}.png", f"{{{{{slug}}}}}", f"{{{slug}}}", } if slug == "background": names.update({"background.png", "sprite_background.jpg", "background.jpg"}) if slug == "player": names.update({"player.png", "sprite_player.jpg", "hero.png"}) if slug == "enemy": names.update({"enemy.png", "monster.png", "sprite_enemy.jpg"}) return namesdef embed_assets(html_code: str, assets: dict[str, str], specs: list[AssetSpec]) -> str: output = html_code manifest_lines = ["<!-- Embedded game assets generated by Image Generator for HTML Games"] for spec in specs: data_uri = assets[spec.role] manifest_lines.append(f"{spec.role}: {spec.filename}") for name in replacement_names(spec): output = output.replace(f'"{name}"', f'"{data_uri}"') output = output.replace(f"'{name}'", f"'{data_uri}'") output = output.replace(name, data_uri) manifest_lines.append("-->") manifest = "\n".join(manifest_lines) + "\n" asset_map = ",\n".join( f" {slugify(spec.role)!r}: {assets[spec.role]!r}" for spec in specs ) helper_script = f"""<script> window.GENERATED_GAME_ASSETS = {{{asset_map} }};</script>""" if "</head>" in output: output = output.replace("</head>", helper_script + "\n</head>", 1) elif "<body" in output: output = output.replace("<body", helper_script + "\n<body", 1) else: output = helper_script + "\n" + output return manifest + outputdef build_preview(html_code: str) -> str: encoded = base64.b64encode(html_code.encode("utf-8")).decode("ascii") return ( f'<iframe src="data:text/html;base64,{encoded}" ' 'style="width:100%;height:560px;border:1px solid #333;border-radius:8px;background:#000;" ' 'sandbox="allow-scripts" title="Game preview"></iframe>' )def generate_images_and_game(html_code: str, roles: str, style_hint: str): if not html_code.strip(): return "", "Paste HTML game code first.", [], "" specs = parse_assets(roles, style_hint or "pixel art style") if not specs: return html_code, "Add at least one asset role, like `player: brave knight`.", [], build_preview(html_code) assets: dict[str, str] = {} gallery = [] errors = [] for index, spec in enumerate(specs): data_uri, error = generate_asset(spec, index) assets[spec.role] = data_uri gallery.append((data_uri, f"{spec.role} -> {spec.filename}")) if error: errors.append(f"{spec.role}: fallback used ({error})") rewritten = embed_assets(html_code, assets, specs) status = f"Generated and embedded {len(specs)} asset(s)." if errors: status += "\n\n" + "\n".join(errors) return rewritten, status, gallery, build_preview(rewritten)with gr.Blocks(title="Image Generator for HTML Games") as demo: gr.Markdown( "# Image Generator for HTML Games\n" "Paste an HTML canvas game, list the image roles you want, and generate a rewritten version " "with the images embedded directly into the code." ) with gr.Row(): with gr.Column(scale=1): roles = gr.Textbox( label="Image roles to generate", value=DEFAULT_ROLES, lines=8, info="One per line: role: image description. Example: player: blue robot hero", ) style = gr.Textbox( label="Shared visual style", value="cohesive 2D pixel-art game style, transparent subject sprites where possible", lines=2, ) generate_btn = gr.Button("Generate Images + Embed Game", variant="primary") status = gr.Markdown("Ready.") gallery = gr.Gallery(label="Generated assets", columns=2, height=300) with gr.Column(scale=2): html_input = gr.Code( label="Original HTML game code", value=STARTER_HTML, language="html", lines=18, ) output_code = gr.Code( label="Rewritten HTML with embedded images", language="html", lines=18, ) gr.Markdown("## Game preview") preview = gr.HTML(build_preview(STARTER_HTML)) generate_btn.click( fn=generate_images_and_game, inputs=[html_input, roles, style], outputs=[output_code, status, gallery, preview], )if __name__ == "__main__": demo.launch()
1a0aaee
verified

LeafCat79 commited on

initial commit
39ce61a
verified

LeafCat79 commited on