Beat Sheet Operations
Beat sheets define a story’s structure as a sequence of narrative beats. Each beat has a title, description, and an optional link to a Scene or Problem element. Beat sheets are applied to Problem elements. A Problem’s structure tab holds the beat sheet data.
Workflow Overview
GetBeatSheetNames → list available templates
GetBeatSheet → preview a template's beats
ApplyBeatSheetToProblem → apply template to a Problem
GetProblemStructure → view the Problem's current beats
AssignElementToBeat → link a Scene/Problem to a beat
CreateBeat / UpdateBeat / DeleteBeat / MoveBeat → customize
SaveBeatSheet / LoadBeatSheet → persist custom beat sheets
Step 1: Browse Templates
var names = api.GetBeatSheetNames().Payload;
foreach (var name in names)
Console.WriteLine(name);
// Three Act Structure
// Hero's Journey
// Save the Cat
// ...
names = sc.get_beat_sheet_names()
for name in names:
print(name)
# Three Act Structure
# Hero's Journey
# Save the Cat
# ...
Step 2: Preview a Template
var result = api.GetBeatSheet("Three Act Structure");
var (description, beats) = result.Payload;
Console.WriteLine(description);
// "The three-act structure divides a story into setup, confrontation, and resolution..."
foreach (var (beatName, beatNotes) in beats)
{
Console.WriteLine($"\n {beatName}");
Console.WriteLine($" {beatNotes}");
}
// Setup
// Introduce the protagonist, setting, and status quo...
// Inciting Incident
// An event disrupts the protagonist's world...
// ...
sheet = sc.get_beat_sheet("Three Act Structure")
for beat in sheet.beats:
print(f"\n {beat.title}")
print(f" {beat.description}")
# Setup
# Introduce the protagonist, setting, and status quo...
# Inciting Incident
# An event disrupts the protagonist's world...
# ...
Step 3: Apply to a Problem
var problemGuid = /* GUID of an existing Problem element */;
var result = api.ApplyBeatSheetToProblem(problemGuid, "Three Act Structure");
if (result.IsSuccess)
Console.WriteLine("Beat sheet applied!");
problem = ... # handle of an existing Problem element
# The wrapper raises on failure instead of returning a success flag.
sc.apply_beat_sheet_to_problem(problem, "Three Act Structure")
print("Beat sheet applied!")
This replaces any existing beats on the Problem. The Problem’s StructureTitle and StructureDescription are set from the template, and StructureBeats is populated with the template’s beats.
Step 4: View the Structure
var structure = api.GetProblemStructure(problemGuid).Payload;
var (title, desc, beats) = structure;
Console.WriteLine($"Structure: {title}");
Console.WriteLine($"Description: {desc}\n");
int index = 0;
foreach (var (beatTitle, beatDesc, linkedElement) in beats)
{
var assignment = linkedElement.HasValue ? $" → {linkedElement}" : " (unassigned)";
Console.WriteLine($" [{index}] {beatTitle}{assignment}");
Console.WriteLine($" {beatDesc}");
index++;
}
structure = sc.get_problem_structure(problem)
for index, beat in enumerate(structure.beats):
print(f" [{index}] {beat.title}")
print(f" {beat.description}")
Output:
Structure: Three Act Structure
Description: The three-act structure divides a story into...
[0] Setup (unassigned)
Introduce the protagonist, setting, and status quo...
[1] Inciting Incident (unassigned)
An event disrupts the protagonist's world...
[2] First Plot Point (unassigned)
...
Step 5: Assign Elements to Beats
Link Scene or Problem elements to beats by their 0-based index.
// Create scenes for the beats
var scene1 = api.AddElement(StoryItemType.Scene, overviewGuid, "Opening Scene").Payload;
var scene2 = api.AddElement(StoryItemType.Scene, overviewGuid, "The Discovery").Payload;
// Assign scenes to beats
api.AssignElementToBeat(problemGuid, 0, scene1); // Beat 0: "Setup"
api.AssignElementToBeat(problemGuid, 1, scene2); // Beat 1: "Inciting Incident"
// Verify
var updated = api.GetProblemStructure(problemGuid).Payload;
foreach (var (beatTitle, _, linked) in updated.Beats)
{
if (linked.HasValue)
Console.WriteLine($" {beatTitle} → {linked}");
else
Console.WriteLine($" {beatTitle} (unassigned)");
}
# Create scenes for the beats
scene1 = sc.add_element(sc.item_type.Scene, overview, "Opening Scene")
scene2 = sc.add_element(sc.item_type.Scene, overview, "The Discovery")
# Assign scenes to beats
sc.assign_element_to_beat(problem, 0, scene1) # Beat 0: "Setup"
sc.assign_element_to_beat(problem, 1, scene2) # Beat 1: "Inciting Incident"
# Verify
updated = sc.get_problem_structure(problem)
for beat in updated.beats:
print(f" {beat.title}")
Finding Elements to Assign
To find existing Scenes or Problems to assign to beats:
// Option 1: Get all scenes
var scenes = api.GetElementsByType(StoryItemType.Scene).Payload;
// Option 2: Search by name
var results = api.SearchForText("confrontation").Payload;
var sceneResults = results.Where(r => r["Type"].ToString() == "Scene");
# Option 1: Get all scenes
scenes = sc.get_elements_by_type(sc.item_type.Scene)
# Option 2: Search by name
results = sc.search_for_text("confrontation")
scene_results = [r for r in results if r["Type"] == "Scene"]
Clearing an Assignment
api.ClearBeatAssignment(problemGuid, 1); // Clears beat at index 1
sc.clear_beat_assignment(problem, 1) # Clears beat at index 1
The beat remains in the structure but has no linked element.
Customizing Beats
Create a New Beat
Adds a beat at the end of the list.
api.CreateBeat(problemGuid, "Epilogue", "The aftermath of the resolution");
sc.create_beat(problem, "Epilogue", "The aftermath of the resolution")
Update a Beat
Changes the title and/or description. Preserves any existing element assignment.
api.UpdateBeat(problemGuid, 0, "Opening Hook", "Grab the reader with an immediate conflict");
sc.update_beat(problem, 0, "Opening Hook", "Grab the reader with an immediate conflict")
Delete a Beat
Removes a beat and its assignment. Remaining beats shift down.
api.DeleteBeat(problemGuid, 5); // Remove beat at index 5
sc.delete_beat(problem, 5) # Remove beat at index 5
Move a Beat
Reorders a beat from one position to another.
api.MoveBeat(problemGuid, 3, 1); // Move beat from index 3 to index 1
sc.move_beat(problem, 3, 1) # Move beat from index 3 to index 1
Saving and Loading Custom Beat Sheets
Custom beat sheets can be saved to .stbeat files and loaded into other Problems.
Save
// Save the Problem's current beat structure to a file
api.SaveBeatSheet(problemGuid, "my-structure.stbeat");
# Save the Problem's current beat structure to a file
# (no documented Python equivalent for save_beat_sheet: see the C# tab)
sc.save_beat_sheet(problem, "my-structure.stbeat")
The Problem must have beats (from ApplyBeatSheetToProblem or CreateBeat).
Load
// Load a .stbeat file into a different Problem
var otherProblemGuid = /* another Problem's GUID */;
api.LoadBeatSheet(otherProblemGuid, "my-structure.stbeat");
# Load a .stbeat file into a different Problem
other_problem = ... # handle of another Problem
# (no documented Python equivalent for load_beat_sheet: see the C# tab)
sc.load_beat_sheet(other_problem, "my-structure.stbeat")
Loading replaces any existing beats on the target Problem. The StructureTitle is set to “Custom Beat Sheet”.
Complete Example
Build a full story structure from a beat sheet template:
// Initialize
BootStrapper.Initialise(headless: true);
var api = Ioc.Default.GetRequiredService<StoryCADApi>();
// Create outline
var outline = await api.CreateEmptyOutline("Mystery Novel", "Author", "0");
var overviewGuid = outline.Payload[0].ToString();
// Create the main problem
var problem = api.AddElement(StoryItemType.Problem, overviewGuid, "Who Killed Mr. Gray?");
var problemGuid = problem.Payload;
// Apply a beat sheet
api.ApplyBeatSheetToProblem(problemGuid, "Three Act Structure");
// View the beats
var structure = api.GetProblemStructure(problemGuid).Payload;
// Create a scene for each beat and assign it
int beatIndex = 0;
foreach (var (beatTitle, beatDesc, _) in structure.Beats)
{
// Create a scene named after the beat
var scene = api.AddElement(StoryItemType.Scene, overviewGuid, beatTitle);
if (scene.IsSuccess)
{
// Set the scene's notes from the beat description
api.UpdateElementProperty(scene.Payload, "Notes", beatDesc);
// Assign the scene to the beat
api.AssignElementToBeat(problemGuid, beatIndex, scene.Payload);
}
beatIndex++;
}
// Add a custom epilogue beat
api.CreateBeat(problemGuid, "Denouement", "The detective reveals the truth");
var epilogue = api.AddElement(StoryItemType.Scene, overviewGuid, "The Reveal");
if (epilogue.IsSuccess)
{
api.AssignElementToBeat(problemGuid, beatIndex, epilogue.Payload);
}
// Save
await api.WriteOutline("mystery-novel.stbx");
# Initialize
sc = StoryCAD(headless=True)
# Create outline
overview = sc.create_empty_outline("Mystery Novel", "Author", template_index="0")[0]
# Create the main problem
problem = sc.add_element(sc.item_type.Problem, overview, "Who Killed Mr. Gray?")
# Apply a beat sheet
sc.apply_beat_sheet_to_problem(problem, "Three Act Structure")
# View the beats
structure = sc.get_problem_structure(problem)
# Create a scene for each beat and assign it
# (the wrapper raises on failure, so no success check is needed)
for beat_index, beat in enumerate(structure.beats):
# Create a scene named after the beat
scene = sc.add_element(sc.item_type.Scene, overview, beat.title)
# Set the scene's notes from the beat description
sc.update_element_property(scene, "Notes", beat.description)
# Assign the scene to the beat
sc.assign_element_to_beat(problem, beat_index, scene)
# Add a custom epilogue beat
sc.create_beat(problem, "Denouement", "The detective reveals the truth")
epilogue = sc.add_element(sc.item_type.Scene, overview, "The Reveal")
sc.assign_element_to_beat(problem, len(structure.beats), epilogue)
# Save
sc.write_outline("mystery-novel.stbx")
Method Reference
| Method | Parameters | Returns | Modifies? |
|---|---|---|---|
GetBeatSheetNames() |
(none) | IEnumerable<string> |
No |
GetBeatSheet(name) |
Template name | (Description, Beats) |
No |
ApplyBeatSheetToProblem(guid, name) |
Problem GUID, template name | bool |
Yes |
GetProblemStructure(guid) |
Problem GUID | (Title, Description, Beats) |
No |
AssignElementToBeat(guid, index, elementGuid) |
Problem GUID, beat index, element GUID | bool |
Yes |
ClearBeatAssignment(guid, index) |
Problem GUID, beat index | bool |
Yes |
CreateBeat(guid, title, desc) |
Problem GUID, title, description | bool |
Yes |
UpdateBeat(guid, index, title, desc) |
Problem GUID, beat index, title, description | bool |
Yes |
DeleteBeat(guid, index) |
Problem GUID, beat index | bool |
Yes |
MoveBeat(guid, from, to) |
Problem GUID, from index, to index | bool |
Yes |
SaveBeatSheet(guid, path) |
Problem GUID, file path | bool |
No (writes file) |
LoadBeatSheet(guid, path) |
Problem GUID, file path | bool |
Yes |