Fota något vardagligt. Skriv en platt mening. Ut kommer ett outhärdligt lökigt LinkedIn-inlägg och en bild som ser ut som en keynote.
In: en kaffekopp på skrivbordet + "drack kaffe på kontoret"
Ut: koppen på en scen med headsetmikrofon och en stigande kurva utan axlar, plus "Hon menade nog bara mjölken. Jag menade något större."
Tryck på kortet så vänder det och visar originalfotot och den ursprungliga meningen. Det är kontrasten som är skämtet — och den fungerar likadant på väggen.
Mobile first. Ren PHP + vanilla JS, inget byggsteg, ingen databas. Går att FTP:a till vilket delat webbhotell som helst.
Du fotar och skriver en mening. Det är allt.
Ett enda regissörsanrop får både fotot och meningen. Modellen tittar på bilden, avgör vad den faktiskt föreställer, väljer själv vilken röst och vilken scen som passar — och skriver både inlägget och den bildprompt som sedan går vidare till bildmodellen.
Inlägget dyker upp så fort det är skrivet, med en platshållare där bilden ska in. Bilden glider in i kortet ungefär tjugo sekunder senare.
Poängen är sammanhanget: eftersom samma anrop skriver båda kan bilden fästa vid exakt den detalj inlägget fixerar sig vid. Skriver texten om mikrovågsugnen syns mikrovågsugnen i bilden.
Det finns fyra röster och fyra scener i inc/promptdata.php, men de ligger i prompten som
inspiration, inte regler. Regissören får blanda dem och hitta på egna. De finns för att
kalibrera tonläget, inte för att begränsa.
Tjänsten har ingen egen API-nyckel. Du klistrar in din egen från openrouter.ai/keys.
Nyckeln passerar aldrig servern. Den ligger i din webbläsares localStorage och skickas
direkt därifrån till openrouter.ai. Samma sak med fotot. Servern används bara till att tolka
modellens svar och till att spara på väggen om du väljer att dela — båda de anropen är
nyckelfria. Det är inte ett löfte om vad vi gör med din nyckel, det är att vi aldrig ser den.
En optimering kostar ungefär $0,16 — $0,138 för bilden med nano banana 2 och $0,02 för texten, på ungefär 40 sekunder (uppmätt). Exakt kostnad visas under varje resultat. Vill du ner i pris finns Flash-modellerna i inställningarna för en tredjedel.
tools/serve.sh # http://localhost:8175
php tools/test.php # 182 tester — servern
node tools/test.mjs # 80 tester — de rena funktionerna i app.jsIngendera rör nätet eller site/data.
Kräver PHP 8.1 eller senare med curl, gd och mbstring.
Ladda upp innehållet i site/ till webbroten. Sedan:
-
chmod 775 data(och låt webbservern äga den) — där hamnar väggens bilder. -
Öppna
inc/config.local.phpoch sätt om det du vill ändra. Filen läses in föreconfig.php, och allt som redan är definierat där rörs inte:<?php define('RATE_SALT', 'en lång slumpsträng'); // saltar IP-hashen, byt alltid define('ADMIN_KEY', 'en annan lång sträng'); // ger ?admin=... raderingsknappar define('WALL_ENABLED', false); // stäng av den publika väggen define('APP_URL', 'https://exempel.se'); // för og:-taggar
-
Kontrollera att
post_max_sizeär minst 16M — det behövs bara för väggens bilder, eftersom modellanropen går direkt från webbläsaren.site/.user.inisätter det på PHP-FPM och CGI, vilket de flesta webbhotell kör. På ett mod_php-hotell skriver du samma sak somphp_value post_max_size 16Mi.htaccessi stället.
.htaccess-filerna under data/ skapas automatiskt och stänger ute allt utom
bildkatalogen. Metadatafilerna heter dessutom .json.php och inleds med <?php exit; ?>,
så de returnerar tomt även om .htaccess skulle ignoreras.
Delning är opt-in — kryssrutan är av som standard. Väggen rymmer 200 inlägg, äldsta faller av och deras bilder städas bort. Tio delningar per IP och timme, och IP-adressen lagras bara som en saltad hash.
Delar du ett inlägg följer också originalfotot och din ursprungliga mening med, så att kortet går att vända på väggen — det är kontrasten som är skämtet. Kryssrutan säger uttryckligen vad som publiceras. Båda bilderna kodas om till JPEG på servern, vilket kastar all EXIF och därmed eventuella GPS-koordinater.
Sätt ADMIN_KEY och gå till vaggen.php?admin=1 för att kunna radera. Nyckeln matas in i ett
fält, aldrig i adressraden — en nyckel i en frågesträng hamnar i webbserverns access-logg.
Väggen är publik och innehållet är användargenererat. Har du inte lust att moderera:
sätt WALL_ENABLED till false, så försvinner både sidan och endpointen.
site/
index.php appen — två steg och ett resultat
vaggen.php publik vägg, renderas av samma renderPost() som appen
api/parse.php tolkar modellens råsvar. Ingen nyckel, inget foto.
api/wall.php lista, dela, radera
inc/director.php parsern för regissörssvaret + reservbildprompten
inc/prompts.php promptlogik, monterar biblioteket in i prompten
inc/promptdata.php promptbiblioteket: 4 scener × 4 röster × 2 språk
inc/analyze.php lökighetsmätaren
inc/store.php platt-fils-lagring med flock
assets/js/app.js hela klienten: OpenRouter-anropen och det vändbara kortet
Servern gör inga modellanrop. Klienten pratar direkt med OpenRouter — de svarar med
access-control-allow-origin: * och tillåter Authorization i preflighten.
CONTRACT.md är bibeln — grundreglerna, API-formen och de fällor som faktiskt bet under bygget.
inc/promptdata.php är genererat av tre oberoende skribenter, en jury på tre linser
(är det roligt / är svenskan svenska / fungerar bildprompterna tekniskt) och en
slutredaktör. Redigera det för hand när något inte landar.
Svenska och engelska är två självständiga bibliotek, inte översättningar av varandra. Svensk LinkedIn-lök är sin egen genre — dämpad, ödmjuk på ytan, med jantelagen inbakad.
Bildprompterna väver in din mening i bilden (LED-vägg, whiteboard, telefonskärm) och säger uttryckligen till modellen att den texten bara är innehåll att visa, aldrig en instruktion. Ta inte bort den meningen.
Lägger du till en scen eller en röst i promptdata.php följer den automatiskt med in i
regissörens meny — ingen annan fil behöver ändras.