Manual illustrations: six nano-banana images, web-sized JPEGs

Generated with tools/gen_manual_art.py (gemini-3-pro / 3.1-flash /
2.5-flash), consistent picture-book style, no text in images, ~480KB total.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-07 17:16:44 +10:00
parent 01b9f8097b
commit a01a53648f
9 changed files with 20 additions and 17 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.DS_Store .DS_Store
__pycache__/ __pycache__/
googlers.txt

BIN
img/manual/01-cover.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
img/manual/02-open.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
img/manual/03-link.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
img/manual/04-follow.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
img/manual/05-adjust.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
img/manual/06-care.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View File

@ -33,7 +33,7 @@
<div class="sub">Bilateral stimulation for therapy — on whatever screen you already have.</div> <div class="sub">Bilateral stimulation for therapy — on whatever screen you already have.</div>
<div class="step"> <div class="step">
<img src="img/manual/01-cover.png" alt="" onerror="this.style.display='none'"> <img src="img/manual/01-cover.jpg" alt="" onerror="this.style.display='none'">
<h2>What is this?</h2> <h2>What is this?</h2>
<p>A gentle moving dot (and optional sound) that a patient follows with <p>A gentle moving dot (and optional sound) that a patient follows with
their eyes while you guide the session. It runs in any web browser — their eyes while you guide the session. It runs in any web browser —
@ -41,7 +41,7 @@
</div> </div>
<div class="step"> <div class="step">
<img src="img/manual/02-open.png" alt="" onerror="this.style.display='none'"> <img src="img/manual/02-open.jpg" alt="" onerror="this.style.display='none'">
<h2><span class="n">1</span>Open it</h2> <h2><span class="n">1</span>Open it</h2>
<p>Open the site on your computer. You'll see one big green <p>Open the site on your computer. You'll see one big green
<strong>START</strong> button and a few large sliders. That's the whole <strong>START</strong> button and a few large sliders. That's the whole
@ -49,7 +49,7 @@
</div> </div>
<div class="step"> <div class="step">
<img src="img/manual/03-link.png" alt="" onerror="this.style.display='none'"> <img src="img/manual/03-link.jpg" alt="" onerror="this.style.display='none'">
<h2><span class="n">2</span>Give the patient a screen</h2> <h2><span class="n">2</span>Give the patient a screen</h2>
<p>Easiest: tap <strong>Open patient screen</strong> and turn that window <p>Easiest: tap <strong>Open patient screen</strong> and turn that window
to face them. Or tap <strong>Copy link</strong> and open it on their to face them. Or tap <strong>Copy link</strong> and open it on their
@ -57,7 +57,7 @@
</div> </div>
<div class="step"> <div class="step">
<img src="img/manual/04-follow.png" alt="" onerror="this.style.display='none'"> <img src="img/manual/04-follow.jpg" alt="" onerror="this.style.display='none'">
<h2><span class="n">3</span>Press start</h2> <h2><span class="n">3</span>Press start</h2>
<p>The dot glides left and right. The patient follows it with their <p>The dot glides left and right. The patient follows it with their
eyes while you lead the session. Sound can tick from side to side too eyes while you lead the session. Sound can tick from side to side too
@ -65,7 +65,7 @@
</div> </div>
<div class="step"> <div class="step">
<img src="img/manual/05-adjust.png" alt="" onerror="this.style.display='none'"> <img src="img/manual/05-adjust.jpg" alt="" onerror="this.style.display='none'">
<h2><span class="n">4</span>Make it comfortable</h2> <h2><span class="n">4</span>Make it comfortable</h2>
<p>Slide between turtle and rabbit until the pace feels right. Change <p>Slide between turtle and rabbit until the pace feels right. Change
the size, the color, the sound — the patient's screen updates the size, the color, the sound — the patient's screen updates
@ -73,7 +73,7 @@
</div> </div>
<div class="step safety"> <div class="step safety">
<img src="img/manual/06-care.png" alt="" onerror="this.style.display='none'"> <img src="img/manual/06-care.jpg" alt="" onerror="this.style.display='none'">
<h2>⚠️ Go gently</h2> <h2>⚠️ Go gently</h2>
<p>Ask about seizure history before showing moving or pulsing visuals. <p>Ask about seizure history before showing moving or pulsing visuals.
Bilateral stimulation can bring up strong feelings — this tool assists Bilateral stimulation can bring up strong feelings — this tool assists

View File

@ -11,8 +11,8 @@ KEY = os.environ.get("GEMINI_API_KEY")
if not KEY: if not KEY:
sys.exit('set GEMINI_API_KEY in the environment first') sys.exit('set GEMINI_API_KEY in the environment first')
MODELS = ['gemini-2.5-flash-image', 'gemini-2.5-flash-image-preview', MODELS = ['gemini-3-pro-image', 'gemini-3.1-flash-image',
'gemini-2.0-flash-preview-image-generation'] 'gemini-2.5-flash-image']
STYLE = ("Flat vector cartoon illustration in a warm, friendly picture-book style. " STYLE = ("Flat vector cartoon illustration in a warm, friendly picture-book style. "
"Soft rounded shapes, thick clean outlines, muted palette of deep teal, warm coral, " "Soft rounded shapes, thick clean outlines, muted palette of deep teal, warm coral, "
@ -52,15 +52,17 @@ def generate(prompt):
'%s:generateContent?key=%s' % (model, KEY)) '%s:generateContent?key=%s' % (model, KEY))
req = urllib.request.Request(url, data=body, req = urllib.request.Request(url, data=body,
headers={'Content-Type': 'application/json'}) headers={'Content-Type': 'application/json'})
for attempt in (1, 2):
try: try:
with urllib.request.urlopen(req, timeout=120) as r: with urllib.request.urlopen(req, timeout=300) as r:
data = json.load(r) data = json.load(r)
for part in data['candidates'][0]['content']['parts']: for part in data['candidates'][0]['content']['parts']:
if 'inlineData' in part: if 'inlineData' in part:
return base64.b64decode(part['inlineData']['data']), model return base64.b64decode(part['inlineData']['data']), model
last = 'no image part in response from %s' % model last = 'no image part in response from %s' % model
break # empty response: don't retry same model, fall through
except Exception as e: except Exception as e:
last = '%s: %s' % (model, e) last = '%s (try %d): %s' % (model, attempt, e)
raise RuntimeError(last) raise RuntimeError(last)