Build a Branching Film Pitch Prototype That Remembers Earlier Choices
Build a Branching Film Pitch Prototype That Remembers Earlier Choices
Two routes can arrive in the same room without becoming the same story. To demonstrate that in a film pitch, give the prototype something to remember: a named decision, a stored value, and a later passage that responds to that value. Then compare it with a version that keeps the histories apart by duplicating passages. The useful choice is not which diagram looks tidier. It is which structure makes the consequences easiest to inspect and revise without breaking them.
The example below, The Last Crossing, is an original fictional text prototype. It targets SugarCube 2.37.3, a Twine/Twee story format, and includes complete source files for both structures.[^1] The sources have passed limited static checks, but they have not been compiled or played in SugarCube. The path table states expected results, not observed results. Native playthrough verification remains necessary before presenting either build as a working demonstration.
Give the choice a consequence before giving it a button
In The Last Crossing, you learn that a departure has been delayed. Mara has arranged for a neighbor to stay with her father until her expected crossing time. You choose whether to send her the warning. Both routes eventually reach a waiting room.
When warned, Mara has brought food and asked the neighbor to stay longer. Without the warning, she is trying to reach the neighbor after the original arrangement should have ended. The departure board, the room, and the delay are unchanged. What differs is what she could prepare for—and the conversation you now have with her.
That is the contract the prototype needs to keep. “The viewer can make choices” would be too broad to test. “An earlier warning changes Mara's preparation in a later shared scene” gives a collaborator something specific to inspect.
Keep the test small. There is no ferry footage, countdown, branching ending, scoring system, or automatic decision when someone does nothing. Those are other design questions. This prototype needs one choice with two consequences, followed by an opportunity to return to the room. Adding ten choices would make it harder to tell whether the first one works.
Write down the two expected versions before building anything. Otherwise a plausible response can look correct merely because it appears after a click. In the withheld route, seeing Mara unwrap food should be a failure, not an acceptable variation you rationalize during the meeting.
Route one: let the passage names carry the history
The duplicated version has two waiting-room passages: WaitingSent and WaitingWithheld. They describe the same physical place. The distinction exists in the prototype's structure, not in the film's geography.
Its relevant map is:
Choice → WaitingSent → CorridorSent → WaitingSent
→ WaitingWithheld → CorridorWithheld → WaitingWithheld
The choice passage uses ordinary links:
:: Choice
The last departure has been delayed. Mara is preparing to travel and has arranged for a neighbor to stay with her father until the expected crossing time.
[[Send the warning|WaitingSent]]
[[Withhold the warning|WaitingWithheld]]
Each destination carries its own version of the scene:
:: WaitingSent
A departure board clicks, but the time does not change.
Mara unwraps the food she packed after your message. "I asked the neighbor to stay longer."
[[Step into the corridor|CorridorSent]]
:: WaitingWithheld
A departure board clicks, but the time does not change.
Mara calls the neighbor again. No answer. "They were supposed to leave when I crossed. Dad is still waiting."
[[Step into the corridor|CorridorWithheld]]
There is no separate warning variable in this build. The path preserves the distinction. Someone inspecting WaitingSent can read its whole version without evaluating a condition or looking elsewhere for the value that selects it.
The corridor passages must remain separate too. CorridorSent returns to WaitingSent; CorridorWithheld returns to WaitingWithheld. Merging them into a single corridor would remove the simple route-based way of deciding which room to return to. You would need to recover the history by another mechanism.
This is the first trade-off worth showing in the pitch conversation. Duplication is not inherently unsophisticated. Here it makes both alternatives explicit and easy to compare. But every additional place that must preserve the distinction needs another pair of passages—or another method of remembering.
There is also a revision cost. Change the departure-board sentence and both waiting-room copies need the edit. Change only the warned response and one copy should change. Those are different operations. Treating them as one global replacement risks erasing an intentional difference; treating everything as a local edit risks allowing the common scene to drift.
The complete duplicated source includes the opening, both corridors, and a restart control. It is a small route comparison, not a recommendation to duplicate an entire interactive film.
Route two: keep one room and carry the decision into it
The conditional version uses one waiting-room passage and one corridor:
Choice — set warning to sent or withheld → WaitingRoom
↓ ↑
Corridor
The current location answers “Where are we?” The stored warning answers “What happened before we arrived?” Separating those questions is what allows literal passage reconvergence without discarding the earlier choice.
SugarCube distinguishes story variables, prefixed with $, from temporary variables, prefixed with _. The Twine Cookbook describes the former as available across passages and the latter as local to the current passage.[^2] This fixture therefore uses $warning, not _warning.
The initial value is deliberately neither of the two valid decisions:
:: StoryInit
<<set $warning to "unmade">>
Then each choice sets its own value before taking the reader to the same destination:
:: Choice
The last departure has been delayed. Mara is preparing to travel and has arranged for a neighbor to stay with her father until the expected crossing time.
<<link "Send the warning" "WaitingRoom">><<set $warning to "sent">><</link>>
<<link "Withhold the warning" "WaitingRoom">><<set $warning to "withheld">><</link>>
StoryInit, the <<set>> and <<link>> macros, and the conditional syntax below are documented SugarCube mechanisms.[^3] The scene content and the choice contract are original to this example.
:: WaitingRoom
A departure board clicks, but the time does not change.
<<if $warning is "sent">>
Mara unwraps the food she packed after your message. "I asked the neighbor to stay longer."
<<elseif $warning is "withheld">>
Mara calls the neighbor again. No answer. "They were supposed to leave when I crossed. Dad is still waiting."
<<else>>
''Prototype diagnostic: no valid warning choice has been recorded. Restart and choose a route.''
<</if>>
[[Step into the corridor|Corridor]]
:: Corridor
You step away from the benches. The departure is still delayed.
[[Return to the waiting room|WaitingRoom]]
Read the three cases separately. The first displays the warned consequence. The second displays the withheld consequence. Anything else displays a diagnostic. An unmade decision, a missing value, and an unexpected value should not quietly impersonate a valid story history.
Do not simplify the last two cases into “sent, otherwise withheld.” That would make a skipped choice look like an intentional refusal to warn Mara. The prototype would still produce readable prose, which is precisely why the error would be easy to miss.
Also notice where the assignments are absent. Neither WaitingRoom nor Corridor sets $warning. Returning to the room should consult the earlier choice, not overwrite it. Putting the initialization at the top of WaitingRoom would erase the information just before the scene needs it.
The complete shared source includes a diagnostic link that bypasses the choice. It exists to expose the third case during review. It is not an additional authored route in the proposed film.
Decide exactly what the stored value means
$warning records a decision in this particular demonstration. It does not measure Mara's trust, your kindness, or whether the relationship will survive. Naming a variable after an emotion would smuggle a larger interpretation into a small mechanical test.
The fixture also makes a deliberate simplification: choosing to send the warning means it reaches Mara in time for her to act. It does not simulate delivery failure, an unread message, or a warning that arrives too late. State that assumption beside the choice contract. Without it, a collaborator could reasonably ask why clicking Send guarantees that she has prepared food.
A later version might need to distinguish an attempted message from a received message. That would require another authored situation and another condition to verify. Do not add the extra variable merely because the software allows it. Add it when the film actually needs the distinction and when a later scene will make that distinction perceptible.
Keep two questions separate during review: Is the history stored correctly? Does the scene express that history? A correct sent value paired with the withheld dialogue is still a failed consequence. So is the right dialogue reached accidentally through the wrong route. Inspect the visible passage against the written contract, not only the variable shown in a developer panel.
The duplicated build needs the same discipline even though it has no explicit warning value. Its passage names are carrying the claim that these histories remain distinct. A misplaced return link can contradict that claim as readily as an incorrect assignment can. Comparing the structures is therefore not a choice between testing and not testing. It is a choice about where the story's memory lives, what must stay coordinated, and how a collaborator can see that coordination fail.
Make the two versions comparable
Keep the scene language identical between implementations. Otherwise a reader may prefer the conditional version because you improved its dialogue, not because its structure suits the project.
A useful comparison starts with two changes on copies of the source files. First, replace the common departure-board sentence with another common sentence. In the duplicated version, make the same edit twice. In the shared version, make it once. Second, alter only Mara's warned response. That change belongs in WaitingSent in one build and the sent arm of WaitingRoom in the other.
After either edit, revisit the withheld route. Its consequence should still be unchanged. After the common-line edit, inspect both routes. They should still describe the same room. These checks connect maintainability to an actual editorial task rather than to a general preference for less code.
Shared passages introduce a different burden. The entire scene is no longer fully specified by its name; it also depends on valid state. A reviewer opening the waiting room in isolation needs to understand the relevant conditions. Duplicated passages expose the alternatives as separate destinations, but their copies need coordination.
For this small fixture, either structure can communicate the decision. Prefer duplication when seeing complete alternatives side by side is the most useful review method and the number of state-dependent locations remains manageable. Prefer the shared version when the same material genuinely needs reuse and the team can keep the state rules visible. Neither route removes the need to test.
Revisit, undo, and restart are three different requests
An in-story revisit means time continues: you step into the corridor and come back. The warning still happened—or still did not happen. The returning passage should preserve that history.
Undo is different. It asks to return to an earlier point in the playthrough. SugarCube's documentation describes history as a sequence of moments containing the active passage and story-variable state.[^3] Test undo with SugarCube's own history controls, not by assuming the browser's Back button is an equivalent narrative action.
A restart asks for a fresh demonstration. It should clear the current playthrough's decision and return to the opening. The supplied sources use SugarCube's confirmation-based restart interface:
:: StoryMenu
<<link "Restart demonstration">><<run UI.restart()>><</link>>
Do not use browser refresh as the reset test. SugarCube documents a playthrough-session restoration mechanism; refresh and a deliberate restart are not interchangeable.[^3] For this fixture, verify both rather than treating a page reload as proof that the initial state has been restored.
Persistence here has a bounded meaning: the decision must survive the specified movement through the current story. It is not a promise about permanent storage, account synchronization, another device, or compatibility with an eventual interactive-video platform.
Run the paths that could expose a convincing mistake
The following is an acceptance table. Every native result is still unrun for the supplied sources. Its job is to make the required outcomes explicit, so the eventual check cannot become “I clicked around and it seemed fine.”
| Check | Expected result | Native result |
|---|---|---|
| Fresh start, send warning | Mara has food and extended the neighbor's stay | Not run |
| Fresh start, withhold warning | Mara tries to reach the neighbor; her father is waiting | Not run |
| Send, corridor, in-story return | The warned response remains | Not run |
| Withhold, corridor, in-story return | The withheld response remains | Not run |
| Undo from the first waiting room to the choice, select the opposite route | The consequence changes to the newly selected route | Not run |
| Restart after either choice; begin again | The opening returns, and neither old choice determines the next route | Not run |
| Shared build: skip the choice using its diagnostic link | The diagnostic appears, not a valid consequence | Not run |
| Shared build: set an invalid warning value during a development test | The diagnostic appears | Not run |
Run the applicable rows in both implementations. Repeat undo from each initial route. Test canceling the restart confirmation as well as accepting it: cancel should not silently discard the current demonstration.
When recording a failure, keep the path and the symptom together. “After withheld → corridor → return, Mara has food” identifies a route leak. “After restart, the room still shows the previous consequence without a new choice” identifies a reset problem. “The diagnostic appears after selecting a valid choice” points toward a setter or navigation problem. These observations are more useful than a screenshot of an attractive opening.
The supplied static checker has verified literal link targets and specific source invariants, including the absence of a warning assignment in the returning room. It does not execute macros, establish browser behavior, or replace any row above.
Put the prototype beside the pitch, not in place of it
To build from the supplied Twee files, use a compiler configured with the intended SugarCube format. Tweego's documentation explains format discovery and --list-formats; check the actual installed format before compiling.[^4] For a setup whose format ID is sugarcube-2, the intended commands are:
tweego --list-formats
tweego -f sugarcube-2 -o duplicated.html duplicated.twee
tweego -f sugarcube-2 -o shared.html shared.twee
These commands have not been executed for this package. Do not turn their presence into a build claim. Record the compiler version, format version, browser, and observed path outcomes when completing the native check.
The explanatory paragraph beside the eventual working prototype can remain short:
This text demonstration follows one decision through a shared scene. Sending a warning changes what Mara can arrange before the delayed departure; withholding it leaves her trying to reach the person looking after her father. The corridor return demonstrates whether that earlier decision remains active. The prototype does not implement film playback, timed choices, an automatic no-choice path, or the complete story.
That paragraph gives the recipient a reason to open the file and a limit on what to conclude from it. Once the runtime checks are complete, keep the tested source with the build. A functioning link is not the result being pitched. The result is the same room, reached with a different history, and a scene that has not forgotten it.
Sources
[^1]: Thomas Michael Edwards, SugarCube, “Downloads,” identifying the 2.37.3 package. Inspected September 19, 2026. Version identification is not a claim that the runtime was installed or executed.
[^2]: Twine Cookbook, Variables, “Story Variables (Harlowe and SugarCube)” and “Temporary Variables (Harlowe and SugarCube).” Inspected September 19, 2026.
[^3]: Thomas Michael Edwards, SugarCube v2 Documentation, StoryInit; <<set>>; <<link>>; <<if>>; UI.restart(); “Guide: State, Sessions, and Saving,” especially “Story History” and “Playthrough Session.” Inspected September 19, 2026. Documentation supports the mechanisms; the original fixture's native behavior is not verified.
[^4]: Thomas Michael Edwards, Tweego Documentation, “Story Formats,” “Search Directories,” and command-line usage. Inspected September 19, 2026. This is a documented build route, not an executed build.
Frequently asked questions
What should the branching prototype demonstrate before it demonstrates more choices?
It should show one named decision, a stored value or route distinction, and a later shared scene that responds to it. In the example, warning Mara changes what she can arrange before a delayed departure; without the warning she is trying to reach the neighbor. The test stays small: no ferry footage, countdown, branching ending, scoring, or automatic no-choice path.
How does the duplicated build remember a decision if it has no warning variable?
The passage names carry the history: WaitingSent and WaitingWithheld, with CorridorSent and CorridorWithheld returning to their matching rooms. Each destination contains its own version of the scene. This makes alternatives explicit and easy to compare, but every place that must preserve the distinction needs another pair of passages or another memory method.
Why not simplify the shared condition to 'sent, otherwise withheld'?
That would make a skipped choice look like an intentional refusal to warn Mara. The fixture uses sent, withheld, and a diagnostic for anything else so an unmade decision, missing value, or unexpected value cannot quietly impersonate a valid story history.
What has and has not been verified for the supplied files?
The sources have passed limited static checks, including literal link targets and an invariant that the returning room does not assign the warning variable. They have not been compiled or played in SugarCube, and the acceptance table's native results are all unrun. The Tweego commands are a documented build route, not an executed build. Runtime checks still need to record compiler version, format version, browser, and observed path outcomes.
How should undo, in-story revisit, restart, and refresh be treated differently?
An in-story revisit continues time: the warning still happened or did not. Undo returns to an earlier point in the playthrough and should be tested with SugarCube's history controls, not assumed to be the browser Back button. Restart asks for a fresh demonstration and should clear the decision and return to the opening. Browser refresh is not the reset test because SugarCube documents playthrough-session restoration.