thats-me/dev/tools/backgrounds/generate_photo.py
Kevin Adametz 522deffa1a Hintergründe: 15 kuratierte Bilder statt Testfotos
Elf universelle Hintergründe von grafisch bis fotografisch (Tinte, Goldstaub,
Horizont, Kohle, Leinen, Nebel, Dämmerung, Nachthimmel, Tusche, Papier,
Aquarell) plus vier v2-Fassungen zur Abstimmung, alle 2048² JPEG mit
320er Vorschauen (7 MB statt 48 MB). Auswahl, Reihenfolge und Helligkeit
stehen in src/config/backgrounds.js; der Picker zeigt Name und aktuelle
Wahl, das Logo folgt der Bildhelligkeit, Presets zeigen auf die neuen
Bilder, alte Pfade werden beim Laden auf den Nachfolger umgeschrieben.
Der Service Worker cacht nur die Vorschauen vor.

Werkzeuge zum Neu-Rendern (SVG-Builder, Gemini-Prompts) liegen in
dev/tools/backgrounds, die Sichtungs-Renders bleiben lokal (gitignore).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-25 09:31:09 +00:00

90 lines
6 KiB
Python

#!/usr/bin/env python3
"""Generates the photographic LifeWave backgrounds with Gemini (Nano Banana 2).
Square, because the app crops with `cover` in both orientations. Every
prompt asks for an empty middle band and no focal point: the wave and its
dots are the subject, the background only sets the mood.
Usage: python3 generate_photo.py [name ...] (default: all five)
"""
import os, sys, time
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'lifeline-images'))
from gemini import generate # noqa: E402
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
OUT = os.path.join(ROOT, 'dev', 'backgrounds', 'photo')
COMMON = (' Square format, fills the frame edge to edge. No text, no watermark, no people, no animals, '
'no buildings, no objects. The centre band of the image is calm and almost uniform, because a '
'thin glowing line will be drawn over it later. Photographic, subdued, low contrast, fine grain.')
PROMPTS = {
# Second pass: the graphic ideas rendered as photographed material, so
# they carry inner detail without gaining a subject.
'01-tinte': (
'Macro photograph of the surface of deep charcoal-black handmade paper washed with black ink, so close that '
'the paper extends beyond every edge of the frame, no sheet edges, no border, no background. '
'Subtle mottled variations of anthracite and graphite, faint cotton fibres visible on close inspection, '
'a soft darker vignette towards the edges, raking light from the upper left giving the surface a gentle '
'matte relief. Nothing written on it, no objects.' + COMMON),
'02-goldstaub': (
'Macro photograph of fine gold dust and a few tiny gold leaf flakes scattered on dark anthracite velvet. '
'The dust gathers along the bottom edge and thins out quickly upwards, the upper two thirds of the frame '
'are almost empty dark velvet with only a handful of distant specks. Warm gold reflections, very shallow '
'depth of field so most specks are soft bokeh. Elegant, dark, sparse.' + COMMON),
'03-horizont': (
'Minimalist photograph of a vast empty plain at the very end of dusk. Black featureless land in the lower '
'third, a deep graphite night sky above, and between them only a thin, soft band of warm amber and gold '
'afterglow along the horizon at two thirds of the height. No sun, no clouds, no trees, no lights. '
'Cinematic, very dark, the glow is the only colour.' + COMMON),
'04-papier': (
'Macro photograph of the surface of warm cream handmade cotton paper, so close that the paper extends beyond '
'every edge of the frame, no sheet edges, no deckle edge, no border, no background. Soft uneven surface with '
'fine fibres, lit with raking light from the side so the texture shows as gentle relief, a subtle '
'darker vignette in the corners. No writing, no lines, no objects, no folds.' + COMMON),
'05-leinen': (
'Photograph of heavy dark charcoal-grey linen with a visible slub weave, lit from the side so the threads '
'catch a faint warm sheen. The fabric lies flat and even across the whole frame, no folds, no creases, no '
'seams, only the weave itself with a very gentle variation of light. Rich, tactile, dark, low contrast.' + COMMON),
'06a-aquarell-warm': (
'Extreme close-up of a watercolour wash on cold-pressed paper, the paper runs beyond every edge of the '
'frame, no border, no tape marks, no stripes, no bands, one continuous sheet. Blooms of warm sepia, golden '
'ochre and a little dusty indigo pigment gather in the corners with visible granulation and soft '
'cauliflower edges, the middle stays pale warm cream. No shapes, no drips, no splatter.' + COMMON),
'06b-aquarell-dunkel': (
'Extreme close-up of an ink wash on heavy paper, the paper runs beyond every edge of the frame, no border. '
'Deep indigo and graphite ink bleeds in from the top and bottom edges with visible granulation, the '
'middle band softens into a muted grey-blue wash, tiny specks of gold pigment caught in the darker '
'areas. Moody, dark overall. No shapes, no drips.' + COMMON),
'07-kohle': (
'Photograph of a dark slate chalkboard surface, matte anthracite with a faint haze of chalk dust and the '
'ghost traces of wiped chalk strokes near the top and bottom edges, the centre almost clean. Fine mineral '
'texture of the slate visible up close, soft even light.' + COMMON),
'08-nebel': (
'Photograph of gentle hills at dawn in dense morning fog, almost monochrome grey-blue, dark toned. '
'A faint treeline is barely visible at the very bottom, the rest of the frame is soft, empty fog with a '
'slight brightening towards the top. No sun, no sky detail.' + COMMON),
'09-daemmerung': (
'Long-exposure photograph of a calm sea at dusk. The horizon sits in the lower third. Deep blue-grey '
'sky above fading into a narrow warm amber band right at the horizon, perfectly smooth water below, '
'darker than the sky. No sun disc, no detailed clouds, no boats, no shore.' + COMMON),
'10-nachthimmel': (
'Photograph of a deep midnight-blue night sky turning to black, with faint thin wisps of high cloud. '
'No stars, no moon, no ground, no horizon, no light source. A smooth dark gradient with a hint of '
'texture from the clouds.' + COMMON),
}
if __name__ == '__main__':
names = sys.argv[1:] or list(PROMPTS)
os.makedirs(OUT, exist_ok=True)
for name in names:
out = os.path.join(OUT, f'{name}.jpg')
for size in ('2K', '1K'):
try:
generate(PROMPTS[name], out=out, aspect='1:1', size=size)
print(out, size, os.path.getsize(out) // 1024, 'KB')
break
except RuntimeError as error:
print(name, size, 'failed:', str(error)[:200])
time.sleep(2)