Press to Play:

Documentation

Narrative

Our visual novel is built around three scenes: the moment when Sonny’s brother learns about Sonny’s arrest, the scene where the two brothers meet and argue, and the scene at the jazz club. We chose these three scenes to follow a traditional three-act structure, where the first act is the setup, the second act the conflict and climax, and the third act the resolution. Through this structure, we aim to emphasize the growth of Sonny’s brother in his relationship with Sonny: from a close stranger in the first scene, where he is still confused about Sonny, to direct confrontation in the second scene, where he forms a concrete idea of how he should treat Sonny, and finally to resolution in the third scene.

Mood Board

Mood Board

Storytelling Structure

We are adopting a wave + wavelet combination to create a main three-act structure while maintaining smaller, constant tensions throughout. These smaller tensions are reflected in the choices the user makes, which oscillate between two opposing but self-justifying values: the authoritative and protective (represented by the lion) and the enlightened and understanding (represented by the elephant).

Medium

We decided to create a visual novel adaptation of Sonny’s Blues because it aligns well with the message we want to convey: the personal growth of the protagonist through his interactions with Sonny. Since growth is typically a more linear process, it works well with a wave structure in a visual novel, which is itself strictly linear. A visual novel also allows this growth to be felt more intimately, as you play from the protagonist’s first-person point of view, gaining insight into his specific thought processes. An intentional stylistic technique we use is to show only the silhouette of the protagonist throughout the game, inspired by a chapter in Understanding Comics, which suggests that greater abstraction gives the reader more room to interpret and position themselves as the character. Additionally, visual novels allow the user to make choices that affect the story. We think this is an excellent way to convey the notion of growth and the wavelet storytelling structure, where your future self is the product of your past actions, often driven by the clash of two opposing values.

Game Mechanisms

Yet, we didn’t want choices to feel arbitrary, so we introduced a mechanism of coherency. This is meant to reinforce the idea that once you make decisions, they begin to shape who you are, and you end up having to live with the results of your previous choices. If past choices predominantly reflect a particular value, thereby increasing coherency, the player is eventually taken over by the animal representing that value, which then forces the player’s future decisions. There is also an open ending, which occurs when the player interprets the story with logical inconsistencies. This is meant to show that not adhering to any single value can offer maximum freedom, but requires relying entirely on one’s own judgment. The three different endings, together with this mechanism, aim to convey the notion of growth.

Process

I was responsible for the plot, the UI design, and the code in Renpy and the assembly of all the elements:

I built the plot by reading Sonny’s Blues and extracting quotes from the novel that would fit as editable sentences (with some additions of my own to improve transitions). There were two principles I followed: the sentence had to reflect the values of authoritative or enlightened depending on whether certain words were removed or kept, and it also had to serve as a significant driver of the plot. I ended up with nine editable sentences:

  1. Sonny is [my brother/a talented musician/an American racing driver who competed in Formula One, apparently].
  2. I didn't want to believe that I'd ever see my brother [going down/suffering/winning Formula 1].
  3. Part of me wants to [comfort/flex on/ask] Sonny.
  4. Sonny [seeks/needs] [understanding/meaning].
  5. When I did, I decided to [write a letter to/meet/completely ignore] Sonny.
  6. I noticed that Sonny [avoided eye contact/seemed lost in thought/grew a funny mustache].
  7. All that [dope/financial/jazz] business.
  8. You don't know how [awful/difficult] it is to be [afraid/yourself].
  9. I finally feel like I [am starting to understand him/may never understand him/…].

I then connected them to form a coherent story, trying to stay faithful to the original novel by mostly using direct quotes.

Next, I implemented the story in Ren’Py. The transitional parts were relatively easy to implement, as they only require basic functions in Ren’Py. They involved adding transitions between scenes, deciding when characters appear, and etc. The editable sentences, however, were more difficult since there is no preexisting structure in Ren’Py for this. I ended up creating my own menu in code. The basic idea is to make the editable words into buttons that trigger a secondary menu, which then changes the value of the word in the sentence. Each choice increases a variable corresponding to the value it represents. Coherency is calculated as the absolute value of the difference between these variables. There were some obstacles, as I am not very familiar with Python and had no idea how to create UI elements(and indentation in python is really annoying), but I eventually figured it out through asking ChatGPT and doing research online.

  
# script.rpy

default sentence_stage = 1

default line_subject = "Sonny"
default line_verb = "is"
default line_object = "my younger brother"

default popup_target = ""
default choice_history = []

default auth_points = 0
default enlight_points = 0
default inconsistencies = 0
default pending_choice = None

style edit_frame is frame
style edit_frame:
    padding (24, 24)


style edit_text is default
style edit_text:
    size 34

style edit_button is button
style edit_button:
 

    background Frame(Solid("#ffffff22"), 2, 2)
    hover_background Frame(Solid("#ffffff55"), 2, 2)
    insensitive_background Frame(Solid("#ffffff11"), 2, 2)

style edit_button_text is default
style edit_button_text:
    size 34
    xalign 0.5
    yalign 0.5

style popup_button is choice_button
style popup_button:
    xfill True
    xmaximum 1000
    padding (12, 10)
    yminimum 60
style popup_button_text is choice_button_text
style popup_button_text:
    xalign 0.0

style continue_button is popup_button
style continue_button:
    xfill False
    xmaximum 420
    xalign 0.5

style popup_title is gui_text
style popup_title:
    size 33
    xalign 0.0

style hint_text is default
style hint_text:
    color "#aaaaaa"
    size 22

default preferences.text_cps = 30

init python:

    def beepy_voice(event, interact=True, **kwargs):
        if not interact:
            return

        if event == "show":
            renpy.sound.play("audio/sounds/beeps.ogg", channel="sound", loop=True)
        elif event == "slow_done" or event == "end":
            renpy.sound.stop(channel="sound")


    def record_choice(tag):
        if tag not in store.choice_history:
            store.choice_history.append(tag)
    
    def apply_choice():
        if store.pending_choice == "authoritative":
            store.auth_points += 1
        elif store.pending_choice == "enlightened":
            store.enlight_points += 1
        elif store.pending_choice == "playful":
            store.inconsistencies += 1

        store.pending_choice = None


        
image elephant first = im.Scale("images/characterSprites/elephant first.png", 675*1.5, 600*1.5)
image macaque first = im.Scale("images/characterSprites/macaque first.png", 475*1.5, 550*1.5)
image lion first = im.Scale("images/characterSprites/lion first.png", 580*0.7*1.5, 800*0.7*1.5)
image elephant second = im.Scale("images/characterSprites/elephant second.png", 675*1.5, 600*1.5)   
image macaque second = im.Scale("images/characterSprites/macaque second.png", 475*1.5, 550*1.5)
image lion second = im.Scale("images/characterSprites/lion second.png", 580*0.7*1.5, 800*0.7*1.5)

image main character = im.Scale("images/characterSprites/main character.png", 1152*0.4*1.5, 1925*0.4*1.5)
image sonny smile = im.Scale("images/characterSprites/sonny smile.png",1429*0.4*1.5,1890*0.4*1.5)
image sonny apprehensive = im.Scale("images/characterSprites/sonny apprehensive.png",1429*0.4*1.5,1890*0.4*1.5)

image bg subway = im.Scale("images/background/subway.png",1920,1080)
image bg apartment = im.Scale("images/background/apartment.png",1920,1080)
image bg jazzclub = im.Scale("images/background/jazzclub.png",1920,1080)
image bg fog = im.Scale("images/background/fog.jpg",1920,1080)
define mc = Character(
    "Brother",
    color="#ffffff",
    callback=beepy_voice,
    image="main character"
)

define va = Character("The Lion", color="#ff8f8f", callback=beepy_voice, cps=40, image="lion first")
define ve = Character("The Elephant", color="#caa6ff", callback=beepy_voice, cps=20 , image="elephant first")
define vp = Character("The Macaque", color="#8fb7ff", callback=beepy_voice, cps=10, image="macaque first")

define s = Character("Sonny", color="#ffcc88", image="sonny apprehensive")


transform half_size:
    zoom 0.5

screen zone_out():
    add Solid("#ffffff80")  

screen stats_hud():
    frame:
        style "edit_frame"
        xalign 0.50
        yalign 0.50

        vbox:
            spacing 6
            text "Authoritative [auth_points]" color "#ff8f8f"
            text "Enlightened [enlight_points]" color "#caa6ff"
            text "Inconsistencies [inconsistencies]" color "#8fb7ff"

default changeObjectStages = [1, 2, 6]
default changeVerbAndObjectStages = [4, 8]
default changeVerbStages = [3, 5, 7]

screen editable_line():
    modal True

    frame:
        xalign 0.5
        yalign 0.35

        xfill False

        vbox:
            xminimum 1500
            yminimum 200
            hbox:
                
                spacing 14
                box_wrap True
                xalign 0.5
                yalign 0.35
                xfill False
               
                
                text "[line_subject]" style "edit_text"

                if sentence_stage in changeVerbStages or sentence_stage in changeVerbAndObjectStages:
                    textbutton "[line_verb]":
                        style "edit_button"
                        text_style "edit_button_text"
                        action [
                            SetVariable("popup_target", "verb"),
                            Show("edit_popup")
                        ]
                else:
                    text "[line_verb]" style "edit_text"

                if sentence_stage in changeObjectStages or sentence_stage in changeVerbAndObjectStages:
                    textbutton "[line_object]":
                        style "edit_button"
                        text_style "edit_button_text"
                        action [
                            SetVariable("popup_target", "object"),
                            Show("edit_popup")
                        ]
                else:
                    text "[line_object]" style "edit_text"

    textbutton "Continue":
                style "popup_button"
                text_style "popup_button_text"
                xalign 0.5
                yalign 0.50
                action [
                    Function(apply_choice),
                    Return("done")
                ]
            
default guide_lock = None  
default final_input = ""

screen edit_popup():
    modal True

    add "gui/nvl.png" xalign 0.5 yalign 0.5

    vbox:
        xalign 0.5
        yalign 0.68
        xmaximum 1080
        spacing 12

        if sentence_stage == 1:
                text "Who is Sonny?" 
                style "popup_title"

                textbutton "my younger brother":
                    style "popup_button"
                    text_style "popup_button_text"
                    action [
                        SetVariable("line_object", "my younger brother"),
                        Function(record_choice, "obj_brother"),
                        SetVariable("pending_choice", "authoritative"),
                        Hide("edit_popup")
                    ]

                textbutton "a talented musician":
                    style "popup_button"
                    text_style "popup_button_text"
                    action [
                        SetVariable("line_object", "a talented musician"),
                        Function(record_choice, "obj_talented"),
                        SetVariable("pending_choice", "enlightened"),
                        Hide("edit_popup")
                    ]

                textbutton "an American racing driver who competed in Formula One, apparently":
                    style "popup_button"
                    text_style "popup_button_text"
                    action [
                        SetVariable("line_object", "an American racing driver who competed in Formula One, apparently"),
                        Function(record_choice, "obj_formula_one"),
                        SetVariable("pending_choice", "playful"),
                        Hide("edit_popup")
                    ]

        elif sentence_stage == 2:
                text "What happened to my brother?" style "popup_title"

                textbutton "going down":
                    style "popup_button"
                    text_style "popup_button_text"
                    action [
                        SetVariable("line_object", "going down"),
                        Function(record_choice, "obj_going_down"),
                        SetVariable("pending_choice", "authoritative"),
                        Hide("edit_popup")
                    ]

                textbutton "suffering":
                    style "popup_button"
                    text_style "popup_button_text"
                    action [
                        SetVariable("line_object", "suffering"),
                        Function(record_choice, "obj_suffering"),
                        SetVariable("pending_choice", "enlightened"),
                        Hide("edit_popup")
                    ]

                textbutton "winning Formula One":
                    style "popup_button"
                    text_style "popup_button_text"
                    action [
                        SetVariable("line_object", "winning Formula One"),
                        Function(record_choice, "obj_formula_one"),
                        SetVariable("pending_choice", "playful"),
                        Hide("edit_popup")
                    ]


        elif sentence_stage == 3:
                    text "What does part of me want to do?" style "popup_title"

                    textbutton "comfort":
                        style "popup_button"
                        text_style "popup_button_text"
                        action [
                            SetVariable("line_verb", "comfort"),
                            Function(record_choice, "verb_comfort"),
                            SetVariable("pending_choice", "enlightened"),
                            Hide("edit_popup")
                        ]

                    textbutton "ask":
                        style "popup_button"
                        text_style "popup_button_text"
                        action [
                            SetVariable("line_verb", "ask"),
                            Function(record_choice, "verb_ask"),
                            SetVariable("pending_choice", "authoritative"),
                            Hide("edit_popup")
                        ]

                    textbutton "flex on":
                        style "popup_button"
                        text_style "popup_button_text"
                        action [
                            SetVariable("line_verb", "flex on"),
                            Function(record_choice, "verb_flex_on"),
                            SetVariable("pending_choice", "playful"),
                            Hide("edit_popup")
                        ]


        elif sentence_stage == 4:

                    if popup_target == "verb":
                        text "Choose the verb:" style "popup_title"

                        textbutton "seeks":
                            style "popup_button"
                            text_style "popup_button_text"
                            action [
                                SetVariable("line_verb", "seeks"),
                                Function(record_choice, "verb_seeks"),
                                SetVariable("pending_choice", "enlightened"),
                                Hide("edit_popup")
 
                            ]

                        textbutton "needs":
                            style "popup_button"
                            text_style "popup_button_text"
                            action [
                                SetVariable("line_verb", "needs"),
                                Function(record_choice, "verb_needs"),
                                Hide("edit_popup"),
                                If(
                                    line_object == "meaning",
                                    SetVariable("pending_choice", "playful")
                                ),
                                If(
                                    line_object == "understanding",
                                    SetVariable("pending_choice", "authoritative")
                                )
                            ]

                    elif popup_target == "object":
                        text "What does Sonny seek/need?" style "popup_title"

                        textbutton "meaning":
                            style "popup_button"
                            text_style "popup_button_text"
                            action [
                                SetVariable("line_object", "meaning"),
                                Function(record_choice, "obj_meaning"),
                                Hide("edit_popup"),
                                If(
                                    line_verb == "seeks",
                                    SetVariable("pending_choice", "enlightened")
                                ),
                                If(
                                    line_verb == "needs",
                                    SetVariable("pending_choice", "playful")
                                )
                            ]

                        textbutton "understanding":
                            style "popup_button"
                            text_style "popup_button_text"
                            action [
                                SetVariable("line_object", "understanding"),
                                Function(record_choice, "obj_understanding"),
                                Hide("edit_popup"),
                                If(
                                    line_verb == "seeks",
                                    SetVariable("pending_choice", "enlightened")
                                ),
                                If(
                                    line_verb == "needs",
                                    SetVariable("pending_choice", "authoritative")
                                )
                            ]


        elif sentence_stage == 5:
                    text "What did I decide to do?" style "popup_title"

                    textbutton "write a letter to":
                        style "popup_button"
                        text_style "popup_button_text"
                        action [
                            SetVariable("line_verb", "write a letter to"),
                            Function(record_choice, "verb_write_letter"),
                            SetVariable("pending_choice", "authoritative"),
                            Hide("edit_popup")
                        ]

                    textbutton "meet":
                        style "popup_button"
                        text_style "popup_button_text"
                        action [
                            SetVariable("line_verb", "meet"),
                            Function(record_choice, "verb_meet"),
                            SetVariable("pending_choice", "enlightened"),
                            Hide("edit_popup")
                        ]

                    textbutton "completely ignore":
                        style "popup_button"
                        text_style "popup_button_text"
                        action [
                            SetVariable("line_verb", "completely ignore"),
                            Function(record_choice, "verb_ignore"),
                            SetVariable("pending_choice", "playful"),
                            Hide("edit_popup")
                        ]
        elif sentence_stage == 6:
                    text "What did I notice?" style "popup_title"

                    textbutton "avoided eye contact.":
                        style "popup_button"
                        text_style "popup_button_text"
                        action [
                            SetVariable("line_object", "avoided eye contact"),
                            Function(record_choice, "object_avoid_eyecontact"),
                            SetVariable("pending_choice", "authoritative"),
                            Hide("edit_popup")
                        ]

                    textbutton "seemed lost in thought.":
                        style "popup_button"
                        text_style "popup_button_text"
                        action [
                            SetVariable("line_object", "seemed lost in thought"),
                            Function(record_choice, "object_lost"),
                            SetVariable("pending_choice", "enlightened"),
                            Hide("edit_popup")
                        ]

                    textbutton "grew a funny mustache.":
                        style "popup_button"
                        text_style "popup_button_text"
                        action [
                            SetVariable("line_object", "grew a funny mustache"),
                            Function(record_choice, "object_mustache"),
                            SetVariable("pending_choice", "playful"),
                            Hide("edit_popup")
                        ]
        elif sentence_stage == 7:
                    text "What was Sonny addicted to?" style "popup_title"

                    textbutton "dope":
                        style "popup_button"
                        text_style "popup_button_text"
                        action [
                            SetVariable("line_verb", "dope"),
                            Function(record_choice, "verb_dope"),
                            SetVariable("pending_choice", "authoritative"),
                            Hide("edit_popup")
                        ]
                    
                    textbutton "financial":
                        style "popup_button"
                        text_style "popup_button_text"
                        action [
                            SetVariable("line_verb", "financial"),
                            Function(record_choice, "verb_financial"),
                            SetVariable("pending_choice", "playful"),
                            Hide("edit_popup")
                        ]

                    textbutton "jazz":
                        style "popup_button"
                        text_style "popup_button_text"
                        action [
                            SetVariable("line_verb", "jazz"),
                            Function(record_choice, "verb_lost"),
                            SetVariable("pending_choice", "enlightened"),
                            Hide("edit_popup")
                        ]

        elif sentence_stage == 8:
                    if popup_target == "verb":
                        text "Choose the adjective:" style "popup_title"

                        textbutton "awful":
                            style "popup_button"
                            text_style "popup_button_text"
                            action [
                                SetVariable("line_verb", "awful"),
                                Function(record_choice, "verb_awful"),
                                Hide("edit_popup"),

                                If(
                                    line_object == "it is to be afraid",
                                    SetVariable("pending_choice", "authoritative")
                                ),

                                If(
                                    line_object == "it is to be yourself",
                                    SetVariable("pending_choice", "enlightened")
                                )
                            ]

                        textbutton "difficult":
                            style "popup_button"
                            text_style "popup_button_text"
                            sensitive guide_lock is None 
                            action [
                                SetVariable("line_verb", "difficult"),
                                Function(record_choice, "verb_difficult"),
                                Hide("edit_popup"),
                                If(
                                    line_object == "it is to be afraid",
                                    SetVariable("pending_choice", "playful")
                                ),
                                If(
                                    line_object == "it is to be yourself",
                                    SetVariable("pending_choice", "enlightened")
                                )
                            ]

                    elif popup_target == "object":
                        text "What does Sonny consider as awful/difficult?" style "popup_title"

                        textbutton "it is to be afraid":
                            style "popup_button"
                            text_style "popup_button_text"
                            sensitive guide_lock is None or guide_lock == "lion"
                            action [
                                SetVariable("line_object", "it is to be afraid"),
                                Function(record_choice, "obj_afraid"),
                                Hide("edit_popup"),
                                If(
                                    line_verb == "awful",
                                    SetVariable("pending_choice", "authoritative")
                                ),
                                If(
                                    line_verb == "difficult",
                                    SetVariable("pending_choice", "playful")
                                )
                            ]

                        textbutton "it is to be yourself":
                            style "popup_button"
                            text_style "popup_button_text"
                            sensitive guide_lock is None or guide_lock == "elephant"
                            action [
                                SetVariable("line_object", "it is to be yourself"),
                                Function(record_choice, "obj_yourself"),
                                Hide("edit_popup"),
                                If(
                                    line_verb == "awful",
                                    SetVariable("pending_choice", "enlightened")
                                ),
                                If(
                                    line_verb == "difficult",
                                    SetVariable("pending_choice", "enlightened")
                                )
                            ]
        elif sentence_stage == 9:

            text "Complete the final sentence." style "popup_title"

            if guide_lock == "lion" or guide_lock == "elephant":

                textbutton "I may never understand him":
                    style "popup_button"
                    text_style "popup_button_text"
                    sensitive guide_lock == "lion"
                    action [
                        SetVariable("line_object", "I may never understand him"),
                        Function(record_choice, "final_lion"),
                        SetVariable("pending_choice", "authoritative"),
                        Hide("edit_popup")
                    ]

                textbutton "I am starting to understand him":
                    style "popup_button"
                    text_style "popup_button_text"
                    sensitive guide_lock == "elephant"
                    action [
                        SetVariable("line_object", "I am starting to understand him"),
                        Function(record_choice, "final_elephant"),
                        SetVariable("pending_choice", "enlightened"),
                        Hide("edit_popup")
                    ]

            else:

                input:
                    value VariableInputValue("final_input")
                    length 140
                    xmaximum 1200
                    style "input"

                textbutton "Confirm":
                    style "popup_button"
                    text_style "popup_button_text"
                    action [
                        SetVariable("line_object", final_input),
                        Function(record_choice, "final_macaque"),
                        SetVariable("pending_choice", "playful"),
                        Hide("edit_popup")
                    ]

                    

        textbutton "Close":
                    style "popup_button"
                    text_style "popup_button_text"
                    action Hide("edit_popup")

transform darkened:
    matrixcolor SaturationMatrix(0.0) 

transform lightened:
    matrixcolor SaturationMatrix(1.5)


label start:
    scene bg fog
    with fade

    show macaque first at left
    vp "Hello. My name is Macaque."
    vp "Parts of the story of Sonny’s Blues are missing."
    vp "Your role is to play as Sonny’s brother and rebuild the story by choosing words and completing sentences."
    vp "As you make choices, two voices will try to influence your decisions."
    
    show macaque first at darkened 
    show lion first at center
    
    vp "The Lion, who is authoritative and practical."
    show lion first at darkened
    show elephant first at right 
    vp "And the Elephant, who is enlightened and sentimental."
    hide lion first
    hide elephant first
    hide macque first 
    show macaque first at left
    show macaque first at lightened
    
    vp "In the end, the version of Sonny’s Blues you create will depend on the choices you make."
    vp "Choose carefully, and try to avoid contradictions in logic, memory, or themes."
    vp "Now, let’s begin."
    hide macaque first 

    with fade
    jump scene_1

label scene_1:
    scene bg subway
    show main character at left
    with fade

    mc "I read about it in the paper, in the subway, on my way to work."
    mc "I couldn't believe it, and I read it again."
    mc "It was not to be believed and I kept telling myself that."
    mc "Then his name became real again."
    mc "Sonny..."
    mc "Who is Sonny to me..."
    hide main character

    pause 0.3

    show screen zone_out
    with Dissolve(2.0)

    show bg fog
    show elephant first at darkened, right
    show lion first at lightened, left
    with fade
    va "It is clear that Sonny is the protagonist’s brother."
    va "The story confirms this later on."
    show elephant first at lightened
    show elephant first at right
    show lion first at darkened
    ve "That may be true, but Sonny seems to mean more in the text than simply a brother figure."
    ve "He identifies more strongly as a musician, and music is the way he expresses himself."
    pause 0.2
    
    $ pending_choice = "authoritative"
    $ sentence_stage = 1
    $ line_subject = "Sonny"
    $ line_verb = "is"
    $ line_object = "my younger brother"

    $ _result = renpy.call_screen("editable_line")

    if line_object == "an American racing driver who competed in Formula One, apparently":
        show macaque first at center,lightened
        show elephant first at darkened
        vp "Are you absolutely sure about that?"
        vp "That seems... slightly inconsistent with Sonny’s Blues."
        pause(1)

    hide elephant first
    hide lion first
    hide macaque first
    hide screen zone_out
    show bg subway
    with Dissolve(0.5)

    show main character at left
    
    mc "yes"
    mc "[line_subject] [line_verb] [line_object]."
    mc "He became real to me again."
    mc "When he was about as old as the boys in my classes his face had been bright and open."
    mc "I wondered what he looked like now."

    pause 1.0

    mc "..."

    pause 1.0

    mc "I didn't want to believe that I'd ever see my brother ..."

    hide main character
    show screen zone_out
    with Dissolve(2.0)

    show bg fog
    show elephant first at darkened, right
    show lion first at darkened, center
    show macaque first at left,lightened
    with fade

    vp "What do you think that he said, Lion?"

    show lion first at lightened
    show macaque first at darkened

    va "I think that he didn't expect his brother to corrupt so badly."
    va "As he claims later: 'I told myself that Sonny was wild, but he wasn't crazy.'"
    va "He didn't think that Sonny would start to take heroin"

    show lion first at darkened
    show macaque first at lightened

    vp "What about you, Elephant?"

    show elephant at lightened
    show macaque first at darkened

    ve "I think he didn't expect his brother to suffer so badly after they departed."

    show elephant at darkened
    show macaque first at lightened
    vp "It must indeed be truly shocking to him."

    # SENTENCE 2
    $ pending_choice = "authoritative"
    $ sentence_stage = 2
    $ line_subject = "I"
    $ line_verb = "didn't want to believe that I'd ever see my brother"
    $ line_object = "going down"

    $ _result = renpy.call_screen("editable_line")

    hide elephant first
    hide lion first
    hide macaque first
    hide screen zone_out
    show bg subway
    with Dissolve(0.5)

    show main character at left

    mc "[line_subject] [line_verb] [line_object]."
    mc "Yet it had happened."
    mc "And here I was, talking about algebra to a lot of boys."
    mc "Maybe it did more for them than algebra could."
    mc "Thinking about Sonny, part of me really wants to..."

    hide main character

    show bg fog   
    with fade

    pause(2.0)
    show macaque first at left,lightened
    with fade

    vp "Sometimes the animals go on vacation."
    vp "Or simply get tired of talking."
    vp "In moments like these, you will have to rely on your own judgment."
    vp "What do you think Sonny's brother wanted to do?"

    $ pending_choice = "enlightened"
    # SENTENCE 3
    $ sentence_stage = 3
    $ line_subject = "Part of me wants to"
    $ line_verb = "comfort"
    $ line_object = "Sonny"

    $ _result = renpy.call_screen("editable_line")

    hide macaque first
    hide screen zone_out
    show bg subway
    show main character
    with Dissolve(0.5)

    mc "[line_subject] [line_verb] [line_object]."
    mc "I wonder what Sonny wanted."
    mc "He must have..."

    hide main character
    show lion first at lightened,center
    show macaque first at darkened, left
    show elephant first at right, darkened
    show bg fog   
    with fade

    va "It is obvious that Sonny needs something from the outside to help him."
    va "He is arrested for heroin."
    va "He is unstable, desperate, and unable to control himself."
    va "Something must intervene in his life before he destroys himself completely."
    show lion first at darkened
    show elephant first at lightened
    ve "No, Lion."
    ve "Sonny is not simply helpless."
    ve "He is searching for something."
    ve "You describe him as if he has no agency at all, but the story suggests otherwise."
    show elephant first at darkened
    show macaque first at lightened
    vp "Now you are faced with a more difficult task."
    vp "This sentence is missing two words instead of one."
    vp "Only one interpretation fully fits the story."
    vp "Choose carefully."

   
    $ pending_choice = "enlightened"
    # SENTENCE 4
    $ sentence_stage = 4
    $ line_subject = "Sonny"
    $ line_verb = "seeks"
    $ line_object = "understanding"

    $ _result = renpy.call_screen("editable_line")

    hide elephant first
    hide lion first
    hide macaque first
    hide screen zone_out
    show bg subway
    show main character at left
    with Dissolve(0.5)

    
    mc "[line_subject] [line_verb] [line_object]."
    mc "..."

    pause(2.0)

    hide main character
    scene black 
    with fade

    jump scene_1_evaluation

label scene_1_evaluation:
    show bg fog
    show macaque first at left,lightened
    with fade

    vp "Now, let’s evaluate how well you have performed your task for the first part of Sonny’s Blues."
    vp "In terms of coherency... You scored..."
    pause(2.0)
    vp "[abs(auth_points - enlight_points)] points!"
    vp "Your coherencey rating is determined by how consistently your choices align with the interpretation of Sonny you have been constructing."
    vp "The more coherency points you have, the more consistent your interpretation of the story is!"
    vp "In terms of inconsistencies... You have made..."
    pause(2.0)
    vp "[inconsistencies] logical or factual errors!"

    if(inconsistencies <= 2 and abs(auth_points - enlight_points) > 1):
        vp "Keep up the good work! Let's move on to the second part of Sonny's Blues!"
    elif(inconsistencies == 4): 
        vp "How???"
        pause(2.0)
        hide macaque first
        show macaque second at left, lightened
        vp "HOW DID YOU MAKE SO MANY STUPID ERRORS!!!!"
        vp "Fine."
        vp "Please try harder next time to make choices that are consistent with your previous options, and make sure to avoid any stupid errors."
    else:
        vp "Please try harder next time to make choices that are consistent with your previous options, and make sure to avoid any stupid errors."
        
    hide macaque second
    show bg black
    with fade

    jump scene_2
        

label scene_2:

    show bg apartment
    show main character
    with fade

    mc "And I didn't know what happened to or reach out to Sonny for a long time."
    mc "When I did, I..."

    hide main character
    show lion first at darkened,center
    show elephant first at right, lightened
    show bg fog   
    with fade

    ve "He probably decided to go and meet Sonny in person."
    ve "It is the best way to support and reconnect with someone."

    show lion first at lightened,left
    show elephant first at right, darkened

    va "Are you out of your mind, Elephant?"
    va "The book just mentioned that they haven't talked in a very long time."

    va "How do you think it would even be possible for him to know where Sonny was?"

    va "They must have communicated through letters first."

    $ pending_choice = "authoritative"
    # SENTENCE 5
    $ sentence_stage = 5
    $ line_subject = "When I did, I decided to"
    $ line_verb = "write a letter to"
    $ line_object = "Sonny"

    $ _result = renpy.call_screen("editable_line")

    hide lion first
    hide elephant first
    show bg apartment
    show main character
    with fade

    mc "[line_subject] [line_verb] [line_object]."

    pause 0.5

    mc "It was just after my little girl died."

    pause 0.5

    mc "Grief had finally connected me and Sonny."

 

    mc "For the first time in years, I wrote him a letter."

    pause 2.0


    mc "And eventually, he wrote back."

    show main character at darkened,left
    show sonny apprehensive at right,darkened
    with fade

    s "You don't know how much I needed to hear from you"

    s "I feel like a man who's been trying to climb up out of some deep, real deep and funky hole."

    s "I got to get outside."

    hide sonny apprehensive
    hide main character
    show main character at center
    with fade

    pause 2.0

    mc "After that, we began writing to each other."

    mc "But even through the letters..."
    mc "I still didn't know what Sonny would look like now."

    pause 0.8

    scene black
    with Dissolve(2.0)

    pause 1.0

    scene bg apartment
    with fade

    show main character

    mc "Then one day, I went to meet him when he came back to New York."

    show main character at left
    show sonny apprehensive at right,darkened

    mc "He came to stay with my family."

    mc "We carried his things upstairs in silence."

    mc "The whole time, I kept watching him."

    mc "I noticed that..."

    pause 0.6

    hide main character
    hide sonny apprehensive
    show bg fog   
    with fade

    $ pending_choice = "authoritative"
    $ sentence_stage = 6
    $ line_subject = "I noticed that"
    $ line_verb = "Sonny"
    $ line_object = "avoided eye contact"

    $ _result = renpy.call_screen("editable_line")

   

    show bg apartment
    show main character
    with fade

    mc "[line_subject] [line_verb] [line_object]."

    mc "Even when he smiled, he seemed tired."

    pause 2.0

    mc "After dinner, the house grew quiet."

    hide main character
    show main character at left
    show sonny apprehensive at right,darkened

    mc "Sonny sat by the window smoking."
    mc "I kept watching him, trying to understand what had happened to him."

    mc "'Tell me,' I said at last."

    mc "All that..."

    hide main character
    hide sonny apprehensive
    show bg fog   
    with fade

    $ pending_choice = "authoritative"
    $ sentence_stage = 7
    $ line_subject = "All that"
    $ line_verb = "dope"
    $ line_object = "business"

    $ _result = renpy.call_screen("editable_line")

    show bg apartment
    show main character
    with fade
    mc "[line_subject] [line_verb] [line_object]."
   
    mc "Why did you start doing this?"

    hide main character
    show main character at left
    show sonny apprehensive at right,lightened

    s "I don't know."
    s "Sometimes people do things because they can't stand what's happening inside themselves."

    show sonny apprehensive at right,darkened

    mc "But you knew what it could do to you."

    show sonny apprehensive at right,lightened

    s "..."
    s "You don't know..."

    pause 4.0

    hide main character
    hide sonny apprehensive

    pause 0.8

    scene black
    with Dissolve(2.0)

    pause 1.0

    scene bg fog
    with fade

    if(inconsistencies < 3 and auth_points > 3):
        show lion second at center, lightened
        with Dissolve(1.0)

        va "Hello."

        va "You have probably noticed that we were away for a while."

        va "And if you are wondering about Elephant..."

        pause 0.5

        va "He is gone now."

        pause 0.8

        va "You have demonstrated that you are capable of making coherent decisions."

        va "Unlike Elephant, you understand that Sonny’s suffering must be confronted directly."

        pause 0.5

        va "This is your reward for remaining consistent."

        pause 0.5

        va "From this point on..."

        va "I will guide your interpretation of the story."

        $ guide_lock = "lion"
        $ pending_choice = "authoritative"
        $ sentence_stage = 8
        $ line_subject = "You don't know how"
        $ line_verb = "awful"
        $ line_object = "it is to be afraid."

    elif(inconsistencies < 3 and enlight_points > 3):
        show elephant second at center, lightened
        with Dissolve(1.0)

        ve "Hello."

        ve "You have probably noticed that we were away for a while."

        ve "And if you are wondering about Lion..."

        pause 0.5

        ve "He is gone now."

        pause 0.8

        ve "You no longer need to worry about being judged for your choices."

        ve "This is your reward for understanding Sonny beyond appearances."

        pause 0.5

        ve "From this point on..."

        ve "I will help guide your interpretation of the story."
        $ guide_lock = "elephant"
        $ pending_choice = "enlightened"
        $ sentence_stage = 8
        $ line_subject = "You don't know how"
        $ line_verb = "awful"
        $ line_object = "it is to be yourself."
    else:

        show macaque second at center, lightened
        with Dissolve(1.0)

        vp "Hello."

        vp "You have made a considerable number of inconsistencies."

        pause 0.5

        vp "At this point, the story has begun to lose its stable structure."

        vp "The version of Sonny's Blues you are constructing no longer fully belongs to Lion or Elephant."

        pause 0.8

        vp "And if you are wondering where they went..."

        pause 0.5

        vp "They are both gone now."

        pause 0.8

        vp "From this point forward, the rest of the story will remain open to interpretation."

        vp "There may no longer be a single coherent version of Sonny."

        pause 0.8

        vp "So please continue."

        vp "And make the decisions that you most identify with."
        $ guide_lock = None
        $ pending_choice = "authoritative"
        $ sentence_stage = 8
        $ line_subject = "You don't know how"
        $ line_verb = "awful"
        $ line_object = "it is to be afraid."

    

    $ _result = renpy.call_screen("editable_line")

    hide macaque second
    hide lion second
    hide elephant second
    show bg apartment
    show main character at left
    show sonny apprehensive at right,lightened
    with fade
    s "[line_subject] [line_verb] [line_object]."

    pause 0.8
    show sonny apprehensive at right,darkened

    mc "After that, neither of us spoke for a while."

    mc "The room felt heavy."
    mc "Like we had both finally said something that had been waiting there for years."

    hide sonny apprehensive
    show main character at left
    show sonny smile at right,darkened
    with fade

    mc "It wasn't a big smile."
    mc "But it was the first real smile I'd seen from him since he came home."

    show sonny smile at right,lightened
    with fade

    s "'Listen,' Sonny said."

    s "I'm playing at a club downtown tomorrow night."

    pause 0.5

    s "Why don't you come and hear me play?"
    
    hide sonny smile
    scene black 
    with fade
    
    jump scene_2_evaluation
    

label scene_2_evaluation:

    if guide_lock == "lion":
        show bg fog
        show lion second at center,lightened
        with fade
        va "The Macaque vanished alongside the Elephant."

        va "There is no longer any need for evaluation."

        va "Proceed to the final scene."
    elif guide_lock == "elephant":
        show bg fog
        show elephant second at center,lightened
        with fade
        ve "The Macaque vanished alongside the Lion."

        ve "There is no longer any need for evaluation."

        ve "Proceed to the final scene."
    else:
        show bg fog
        show macaque first at center,lightened
        with fade
        vp "Now, let’s evaluate how well you have performed your task for the second part of Sonny’s Blues."
        vp "In terms of coherency... You scored..."
        pause(2.0)
        vp "[abs(auth_points - enlight_points)] points!"
        vp "In terms of inconsistencies... You have made..."
        pause(2.0)
        vp "[inconsistencies] logical or factual errors!"

        pause 0.8

        hide macaque first
        show macaque second at center,lightened

        vp "Interesting."

        vp "Even after all this..."
        vp "Your version of Sonny is still incomplete."

        pause 0.6

        vp "Perhaps that is unavoidable."

        vp "The story was never really about reconstructing Sonny exactly as he was."

        vp "It was about how people interpret suffering, life, and death."

        pause 0.8

        vp "Lion wanted certainty."

        vp "Elephant wanted understanding."

        vp "But neither of them could fully explain Sonny on their own."

        pause 0.8

        vp "And now they are gone."

        vp "So whatever remains of Sonny..."
        vp "Will depend entirely on you."

        pause 1.0

        hide macaque second 

        show macaque first at center,lightened

        vp "Proceed to the final scene."

    hide macaque second
    hide elephant second
    hide lion second
    show bg black
    with fade

    
    jump scene_3


label scene_3:
    show bg jazzclub
    show main character at left
    with fade

    mc "The club was small and dark."

    mc "Smoke drifted through the room."
    mc "People talked quietly over the sound of glasses and music."

    pause 0.8

    show sonny smile at right,darkened
    with Dissolve(1.0)

    mc "Then Sonny walked to the piano."

    pause 0.8

    s "..."

    pause 1.0

    show sonny smile at right,lightened
    with fade

    mc "And when he began to play..."

    mc "It felt like he was saying everything that neither of us knew how to say before."

    pause 0.8

    hide sonny smile
    hide main character
    scene black
    with Dissolve(2.0)

    pause 1.0

    scene bg fog
    with fade
   

    if guide_lock == "elephant":
        $ pending_choice = "enlightened"
        $ sentence_stage = 9
        $ line_subject = "I finally"
        $ line_verb = "feel like"
        $ line_object = "I am starting to understand him"

        $ _result = renpy.call_screen("editable_line")


        mc "[line_subject] [line_verb] [line_object]."

        pause 1.2


        scene black
        with Dissolve(3.0)

        mc "For the first time, Sonny no longer felt distant to me."

        mc "Not because I solved him."
        mc "But because I finally listened."

        pause 1.0

        scene black
        with Dissolve(3.0)

        centered "{size=+20}ENDING — UNDERSTANDING{/size}"


    elif guide_lock == "lion":
        $ pending_choice = "authoritative"
        $ sentence_stage = 9
        $ line_subject = "I finally"
        $ line_verb = "feel like"
        $ line_object = "I may never understand him"

        $ _result = renpy.call_screen("editable_line")

        mc "[line_subject] [line_verb] [line_object]."
        pause 1.2


        scene black
        with Dissolve(3.0)


        mc "Maybe Sonny was never someone I could completely understand."

        mc "Maybe suffering always leaves something unreachable inside a person."

        pause 1.0

        scene black
        with Dissolve(3.0)

        centered "{size=+20}ENDING — DISTANCE{/size}"


    else:
        $ sentence_stage = 9
        $ line_subject = "I finally"
        $ line_verb = "feel like"
        $ line_object = final_input

        $ _result = renpy.call_screen("editable_line")

        mc "[line_subject] [line_verb] [line_object]."

        mc "Part of me couldn't stop thinking about the choices."

        mc "About how every sentence seemed to push me toward a certain version of Sonny."

        mc "A suffering Sonny."
        mc "A faithful Sonny."
        mc "A coherent Sonny."

        pause 0.8

        mc "Yet..."
        mc "Perhaps life isn't predictable all the time."
        mc "There are things always open to change and uncertainty."

        pause 1.0

        scene black
        with Dissolve(3.0)

        centered "{size=+20}ENDING — Creative{/size}"
        

    
    return

  

As for the UI I used a template on itch.io

The difficulty lay in applying the graphic elements from the template to the customized editable-sentences menu. I spent some time exploring the template’s assets and further customizing them so that they would integrate smoothly with the editable sentences.

Hubert drew the illustrations for the characters and scenes:

User testing