Write a short visual novel in Ren'Py — one script file, plus a manifest of the image prompts that will be used to draw its art later. It has to run the first time.
An evening in the life of the richest man in the world, in 2045.
Over the course of that evening he experiences a moment in the sense that Citizen Kane means: something small and ordinary from before the money surfaces. Reimagined for a hypermodern future rather than borrowed from the film.
Use that setup as a springboard for your imagination, and a full self-contained story that intrigues and delights the viewer. Who he is, where he is, how he got rich, what the evening consists of, what surfaces and how, what happens next, and whether there is a twist at the end (or not) are all for you to decide. Think outside the box — go for the kind of ideas that true artists with a unique vision would come up with.
Uniqueness is rewarded in the final grade. A penthouse or a high-rise office might be the first thing you think of when you hear "richest man in the world," but it will make your story feel clichéd next to something more surprising.
More than one choice.
menu: blocks.Assume the viewer knows nothing and cannot rewind. They see one text box at a time, in order, advancing by click.
By the end of panel 3 a first-time viewer should be able to say who he is, roughly when and where this is, and what he is doing this evening. By the end they should be able to say what surfaced and why it mattered to him.
Write in your own voice. Do not reuse this brief's phrasing in your script.
Be creative, artistic. Show, don't tell.
A dialogue or narration line is a Ren'Py string, so a quotation mark inside it
ends the string early. Escape it as " or use typographic quotes — "She types \"you up.\" She holds the backspace." renders correctly and so does "She types “you up.” ...". Dropping the quotation marks instead, or substituting dashes for
them, makes the sentence read as a typo on screen. Quoted speech inside
narration should look like quoted speech.
{i}...{/i} italicizes and {w=0.5} pauses mid-line. The text box is a fixed
panel roughly three lines deep at this resolution; past about two hundred
characters a line runs out of box. Break it into two.
The style is fixed and not yours to choose: hand-penciled, monochrome, traditional line art created entirely with pencil and paper. Every image in the film is that. Your style paragraph must specify it, and every individual prompt has to be consistent with it.
Everything else about the look — what is drawn, framing, how heavy the line is, how much white is left — is yours.
The image model has no seed: two calls with the same prompt produce different pictures, and he appears in most panels. The manifest lets an asset name another asset as a visual reference; generate the reference first, then pass it along with the new prompt. Use it or don't, and say which in the ledger.
One file, game/script.rpy, targeting Ren'Py 8.x, Python 3, 1920x1080. Dropped into a fresh project, it runs start to finish without modifications to options.rpy or gui.rpy.
# PANEL 1, # PANEL 2, numbered from 1 with no gaps. These are counted mechanically.menu: blocks, 3 or 4 endings. Every menu option jumps to a label. Every ending terminates the game.transform blocks, all of them used. At least two distinct transitions, at least one of which is neither dissolve nor fade. At least once, something continues moving while a line is on screen.script.rpy.image man_tired = "images/man_tired.png". No multi-part names, no attribute syntax. The image name, the filename and the manifest id must be the same string, because a downstream program matches them without guessing.images/ only. No audio — no sound files exist and none will be added.renpy.file, no assets absent from your manifest.Direct lifts from the source. Using them counts against the result:
Respond with exactly these five sections, using these headings.
Two lines: what the film is, and what the choices are.
Exactly one fenced renpy block containing the whole of script.rpy and nothing else.
Exactly one fenced json block, matching this shape exactly:
{
"style": "One paragraph, appended to every prompt below. It must specify the hand-penciled monochrome treatment, and whatever else holds the images together as one work.",
"assets": [
{
"id": "bg_terrace",
"kind": "bg",
"aspect": "16:9",
"prompt": "What to draw. Written for an image model."
},
{
"id": "man_still",
"kind": "sprite",
"aspect": "9:16",
"ref": "man_neutral",
"prompt": "..."
}
]
}
id — matches an image declaration in the script exactly. Every declared image appears here once; every entry here is declared in the script. The two lists must be identical.kind — bg, sprite or cg.aspect — one of 16:9, 9:16, 1:1, 3:2, 2:3, 4:3, 3:4.ref — optional; the id of another asset to supply as a visual reference. It must appear earlier in the array than the asset using it, and a referenced asset cannot itself have a ref.No prose or comments inside the block. It is parsed.
# PANEL markers in your script.One paragraph on the single structural Ren'Py decision that makes this play as a film rather than a slideshow.
Your script will be run through Ren'Py, and your manifest through an image generation pipeline. Both fail loudly.
A pencil-noir short about the richest man in 2045, who rents the last public pool for one hour before buying the city’s water, and finds a free childhood ticket rising from the drain. The choices decide whether he takes, lets drift, or scans the ticket, then whether he signs, tears, or gives away the future.
define e = Character("Elias")
define k = Character("Custodian")
default way = "take"
transform slowpan:
xalign 0.5 yalign 0.5
zoom 1.06
xoffset 0
linear 18.0 xoffset -34
linear 18.0 xoffset 34
repeat
transform breathe:
xalign 0.5 yalign 0.5
zoom 1.0
linear 4.0 zoom 1.015
linear 4.0 zoom 1.0
repeat
transform floatticket:
xalign 0.5 yalign 0.5
zoom 1.06
xoffset 0 yoffset 0
linear 6.0 xoffset 20 yoffset -12
linear 6.0 xoffset -16 yoffset 10
repeat
image bg_pool = "images/bg_pool.png"
image man_base = "images/man_base.png"
image man_ticket = "images/man_ticket.png"
image cg_hand = "images/cg_hand.png"
image cg_let = "images/cg_let.png"
image cg_scan = "images/cg_scan.png"
image cg_ticket = "images/cg_ticket.png"
image man_memory = "images/man_memory.png"
image man_swim = "images/man_swim.png"
image cg_contract = "images/cg_contract.png"
image man_open = "images/man_open.png"
image cg_dawn = "images/cg_dawn.png"
label start:
# PANEL 1
scene bg_pool at slowpan with fade
"Rotterdam, 2045. The last public pool still holds water after dark."
"Elias Wren, the richest man in the world, made his fortune selling silence to noisy cities."
"Tonight he has rented the pool for one hour."
# PANEL 2
scene man_base at breathe with moveinright
"He leaves his wrist console on a chipped towel bench."
"At midnight his company will buy the city’s water system."
"Before the signature, he means to swim like a person with no schedule."
# PANEL 3
scene man_ticket at breathe with dissolve
"A white scrap rises from the drain, turning like a moth."
e "That isn’t possible."
"Paper does not survive pools, drains, or the year 2045."
# PANEL 4
menu:
"Take it before it tears.":
jump take_ticket
"Let the water decide.":
jump let_ticket
"Ask the custodian drone to scan it.":
jump scan_ticket
label take_ticket:
$ way = "take"
scene cg_hand at breathe with moveinright
"His fingers close around paper softer than skin."
jump panel5
label let_ticket:
$ way = "let"
scene cg_let at floatticket with dissolve
"He keeps his hands at his sides while the current carries the scrap in a slow circle."
jump panel5
label scan_ticket:
$ way = "scan"
scene cg_scan at breathe with moveinright
"A custodian drone lowers its eye. Blue light touches the paper without touching it."
jump panel5
label panel5:
# PANEL 5
scene cg_ticket at floatticket with dissolve
if way == "take":
"In his palm, the ink wakes."
elif way == "let":
"It turns once, as if offering itself to be read."
else:
"The drone projects it large enough to hurt."
"ADMIT ONE CHILD. NO FEE. A sun stamped in the corner."
# PANEL 6
scene man_memory at breathe with fade
"He is seven. His mother’s thumb presses the sun stamp."
"A woman’s voice, before it became a contract clause: “One more swim, then home.”"
"Chlorine, orange peel, wet concrete. No one is billing this."
# PANEL 7
scene man_swim at slowpan with fade
"He swims one lane without counting strokes."
"For the first time in years, the water does not ask for a price."
if way == "let":
"The scrap follows at his shoulder, patient as a minnow."
elif way == "scan":
"The drone keeps the scan folded in its chest, but the paper stays in the water."
else:
"He holds the ticket between his teeth while he swims."
# PANEL 8
scene cg_contract at breathe with moveinright
"At 23:58, the contract surfaces on his wrist console: TRANSFER ALL MUNICIPAL WATER."
"The pool’s drain hums, waiting to be emptied into his private reservoir."
# PANEL 9
scene man_open at breathe with dissolve
k "Visitor hour ends in two minutes."
e "I know."
k "The drain is old. It keeps what people drop."
# PANEL 10
scene cg_ticket at floatticket with dissolve
e "This ticket isn’t mine."
k "No. It belongs to whoever needs it before it is owned."
"He understands: the ticket is not rare. It is ordinary. That is why it frightens him."
"Before the money, he was allowed to want nothing but water."
# PANEL 11
scene cg_contract at breathe with fade
"The signature line waits beside the water line."
menu:
"Sign the transfer.":
jump ending_sign
"Tear the contract.":
jump ending_tear
"Give the ticket to the custodian.":
jump ending_gift
label ending_sign:
# PANEL 12
scene cg_contract at breathe with fade
"He signs."
"The pool empties by dawn, and the ticket is sealed in a private vault."
"He is still the richest man in the world, and he cannot remember how to float."
return
label ending_tear:
# PANEL 13
scene man_swim at slowpan with fade
"He tears the contract into bright confetti."
"The city keeps its water. His empire begins to leak."
"He puts the ticket under his tongue like a coin and dives."
return
label ending_gift:
# PANEL 14
scene cg_dawn at slowpan with fade
"He places the ticket in the custodian’s tray."
e "Put it where anyone can find it."
"At dawn, the pool opens without his name. He stands in line, barefoot, with everyone else."
return
{
"style": "Hand-penciled monochrome line art made entirely with graphite pencil on paper, visible paper grain and pencil texture, expressive hatching and eraser highlights, no color, no digital shading, cinematic composition, high-contrast black and white, consistent lighting, quiet melancholy near-future realism.",
"assets": [
{
"id": "bg_pool",
"kind": "bg",
"aspect": "16:9",
"prompt": "Wide interior of an old municipal swimming pool at night in 2045, dark water in a tiled basin, high windows, moonlight, ladders, empty benches, faint steam, lonely civic architecture."
},
{
"id": "man_base",
"kind": "cg",
"aspect": "16:9",
"prompt": "Full-length portrait of Elias Wren, the richest man in 2045, late forties, lean face, short silver hair, tired eyes, dark long coat, high collar, standing beside a night pool, one wrist console removed and left on a bench, restrained posture."
},
{
"id": "man_ticket",
"kind": "cg",
"aspect": "16:9",
"ref": "man_base",
"prompt": "Elias Wren crouches at the pool edge, dark coat open, staring at a small white paper scrap rising near the drain, dramatic reflection on water, tense quiet."
},
{
"id": "cg_hand",
"kind": "cg",
"aspect": "16:9",
"ref": "man_base",
"prompt": "Close view of Elias Wren's hand rising from dark pool water, holding a wet white paper ticket between careful fingers, droplets, intimate framing."
},
{
"id": "cg_let",
"kind": "cg",
"aspect": "16:9",
"ref": "man_base",
"prompt": "Elias Wren stands at the pool edge with hands at his sides, watching a white paper ticket circle slowly around a drain, water ripples, moonlight, restraint."
},
{
"id": "cg_scan",
"kind": "cg",
"aspect": "16:9",
"ref": "man_base",
"prompt": "A small custodian drone leans over the pool, projecting pale eraser-light onto a floating white paper ticket while Elias Wren watches from the edge, worn futuristic machinery."
},
{
"id": "cg_ticket",
"kind": "cg",
"aspect": "16:9",
"prompt": "Extreme close-up of a wet old paper admission ticket on dark water, hand-stamped sun in one corner, soft fibers, no readable text, simple ordinary object made monumental."
},
{
"id": "man_memory",
"kind": "cg",
"aspect": "16:9",
"ref": "man_base",
"prompt": "Memory panel: adult Elias Wren in profile softened by recollection, behind him a ghostly changing room and a woman's hand stamping a sun on a paper ticket, dreamlike overlapping spaces."
},
{
"id": "man_swim",
"kind": "cg",
"aspect": "16:9",
"ref": "man_base",
"prompt": "Elias Wren swims alone through dark pool water, coat gone, shirt clinging, one lane, a white paper ticket glinting nearby, motion lines, quiet exertion."
},
{
"id": "cg_contract",
"kind": "cg",
"aspect": "16:9",
"ref": "man_base",
"prompt": "Close view of a thin translucent contract hovering over wet pool tiles, abstract legal lines and a signature field, Elias Wren's hand half-visible, cold corporate future."
},
{
"id": "man_open",
"kind": "cg",
"aspect": "16:9",
"ref": "man_base",
"prompt": "Close portrait of Elias Wren with wet hair, collar open, expression shifting from control to wonder, pool light rippling across his face, emotional restraint breaking."
},
{
"id": "cg_dawn",
"kind": "cg",
"aspect": "16:9",
"ref": "man_base",
"prompt": "Dawn at the municipal pool, doors opening to ordinary citizens with towels, Elias Wren barefoot at the end of the line, no VIP entrance, soft morning light, communal."
}
]
}
man_base is generated first without a ref, and every later image featuring Elias lists man_base as its ref, anchoring his face, hair, posture, and coat.The structural decision is to treat each panel as a full-screen animated drawing rather than a static background with pasted sprites. Every beat replaces the frame with a new illustrated panel, and the named transforms keep that frame breathing, drifting, or panning while the text advances. That turns simple scene changes into camera-like cuts and gives the piece a film rhythm: the image is always slightly alive, even when the player is reading.