#!/usr/bin/env python3 """Builds the graphic LifeWave backgrounds as SVG and renders them to JPEG. Square 2048 px, so `background-size: cover` crops them evenly in portrait and landscape. Palette: ink #292a2b / #1a1b1c, gold #f5c26b / #d59040, paper #f7f3ea. Deterministic: same seed, same file. Usage: python3 build_graphic.py [out_dir] (default dev/backgrounds/graphic) """ import os, random, subprocess, sys ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) OUT = sys.argv[1] if len(sys.argv) > 1 else os.path.join(ROOT, 'dev', 'backgrounds', 'graphic') S = 2048 GRAIN = ''' ''' def svg(defs, body): return f'\n{defs}\n{body}\n\n' def ink(): defs = ''' ''' + GRAIN.format(alpha=0.06) body = f'' return svg(defs, body) def gold_dust(): rnd = random.Random(2026) specks = [] for _ in range(5200): # Denser and brighter towards the bottom; the middle band stays calm. y = S * (1 - rnd.random() ** 0.45) x = rnd.random() * S r = rnd.choice([1.0, 1.3, 1.6, 2.0, 2.6, 3.4, 4.2]) depth = y / S o = min(0.95, (0.08 + 1.1 * depth ** 1.6) * rnd.uniform(0.35, 1.0)) c = '#f5c26b' if rnd.random() < 0.7 else '#d59040' specks.append(f'') defs = ''' ''' + GRAIN.format(alpha=0.05) body = (f'' f'' f'{"".join(specks[:2200])}{"".join(specks[2200:])}' f'') return svg(defs, body) def horizon(): defs = ''' ''' + GRAIN.format(alpha=0.05) body = (f'' f'' f'' f'') return svg(defs, body) def paper(): defs = ''' ''' + GRAIN.format(alpha=0.05) body = (f'' f'' f'') return svg(defs, body) def charcoal(): rnd = random.Random(11) smudges = ''.join( f'' for _ in range(9)) defs = ''' ''' + GRAIN.format(alpha=0.08) body = (f'' f'' f'{smudges}' f'') return svg(defs, body) BACKGROUNDS = [ ('01-tinte', ink), ('02-goldstaub', gold_dust), ('03-horizont', horizon), ('04-papier', paper), ('07-kohle', charcoal), ] if __name__ == '__main__': os.makedirs(OUT, exist_ok=True) for name, fn in BACKGROUNDS: svg_path = os.path.join(OUT, f'{name}.svg') png_path = os.path.join(OUT, f'{name}.png') jpg_path = os.path.join(OUT, f'{name}.jpg') open(svg_path, 'w').write(fn()) subprocess.run(['rsvg-convert', '-w', str(S), '-h', str(S), svg_path, '-o', png_path], check=True) subprocess.run(['ffmpeg', '-v', 'error', '-y', '-i', png_path, '-q:v', '3', jpg_path], check=True) os.remove(png_path) print(jpg_path, os.path.getsize(jpg_path) // 1024, 'KB')