It's no secret that we've made more than a few narrative games for LD now. What's perhaps less apparent is HOW we make them, or more precisely: "How do you make narrative games with branching outcomes in 3 days and not go insane?"
Lets take a wonderful journey through the history of our narrative LD games. What worked, what didn't, and how I changed my approach each time.
The first stab I ever took at telling an actual narrative was waaaay back in LD34. There was an attempt with an earlier entry actually, but it's mostly been lost to time (and it was mostly just a last minute joke) so lets start here for the sake of cohesiveness. LD34 predates the new site, so a direct link to the entry can be found here.
In this entry, I originally just wanted a way to show the controls and some hints, but at the last minute I added a simple story to it.

Implementation wise, there's not a whole lot going on here. The levels are defined with a length in pixels, and at any given offset we can inject text into it.

In this example, there is a bit at the beginning of the level, and a bit more halfway through. The actual levels have predefined patterns, so the dialogue is placed at times where no ships are on grid, and we can pause the timer until the player clicks through the dialogue.
Next comes LD36. Once again, this predates the new site, so here's a link to that entry. This was my first Jam, complete with an actual artist, so I had a surprising amount of extra time, which I used to tell a more detailed story. For this most part, the technical approach was the same as the LD34 game, but since this game had lots of menus, there were more places to drip in additional pieces of narrative. For example, the shop:

Shop items all have little bits of background info in them, which helps build in world building. The shopkeeper also has a pool of dialogue he selects from on top of the usual "Welcome!". Nothing magic here, just a quick string array.

Which brings us to LD40.

With Lovely Kana, we were writing what was essentially a text adventure game, so there was a stronger need for dialogue variation and, more critically, a need for players to make choices. So I wrote this abomination:

No, the code hasn't rotted, it just really looked that bad for reasons. Anyway, this system essentially existed to build a condition based event pool. For each phase of the game, it would run through all of the possible events in a given group, then discard any that didn't meet the requirements given the current gamestate. From the remaining events, one was selected and run.
This works quite well at a glance, and it let us build a wide variety of events quickly. However, a lot of time was wasted building the tool itself, and it was difficult to visualize which events applied when. There was always the risk that changing a stat somewhere could create a "gap" where no events could match certain situations, or that an event would fire at the wrong time due to a typo. The tool itself also just kinda sucked; it's pretty hard to write a comfortable to use tool and the rest of your game inside the LD window, and I had to do both the code and the writing, so the actual tool was mostly just wasted effort.
Enter LD43

Silent whisper took what we learned from Lovely Kana, and doubled down on it, with a longer, darker, and more cohesive story. Rather than just being purely random events, the game was more a series of events and sacrifices you had to make to get to a finale.
The biggest change here was in how the writing was actually done. Rather than wasting time on a gui, the events were written in a giant XML file. In here, comments could be placed and events could be organized more logically. This didn't actually solve most of the problems from LD40, but did save a lot of time, as the time saved not writing a GUI could be invested into writing instead. The XML gave just as much insight as the old GUI, if not moreso, and could be more quickly updated to match changing requirements.

If there was a lesson from LD43, one might think it would be "Don't write GUIs". But what it really taught us was that the problems we faced with Kana weren't just one-offs. Even though I had more time to write, the script complexity issue was only amplified by it. It became this black box of data, impenetrable to even the one who wrote it. What would happen if I adjusted this number? no idea. Are there any bigs? probably. Can I find them? Only if I hit them during testing. Even then, the solution might just create another bug.
In retrospect. It's a miracle Silent Whisper worked at all. But I knew after that something had to change. And thus began my search for the Perfect Tool.
...which didn't exist. Sure, there are tools out there for writing dialogue in games, like Yarn Spinner and Twine, but none of them really did the things I wanted them to do. And so I wrote one.
Its debut was for LD45

With Zermatt: Zero, we had a goal in mind to push our narrative skills, and this new tool was going to be the way we did it. Calling back to retro visual novels, we had to deal with a lot of branching dialogue, game states, sequences, and other problems that would be extremely difficult to map out in text alone, so the solution was nodes.

This tool served its purpose well. Being able to more easily visualize the game flow through nodes made it much easier to quickly deduce state at any given time. Whole sections could be organized and discarded without worrying about breaking unrelated parts of the game, and even debugging was easy, since you could quickly jump through the story.
Another key aspect was finding the right balance between code and node. The goal here wasn't to re-invent blueprints; just to move high level game flow into a place it could be more conveniently visualized and edited. Essentially, nodes can do things like change what scene the game is in, set the CG, music, prompt the user for choices, and display dialogue. The game itself was responsible for actually responding to those events, and handling all the finer details of those interactions (like when to show movement controls, etc). There's always a cost to placing each node, so make it so only had to place as many as you needed to control the flow of the game. Busy work should always be moved to code.
This tool saw some incremental updated through subsequent LDs, with the addition of containers for better organizing, improvements to the UX, and adding additional features to it such as remote console and debugging features. But there were still problems. One of the biggest was simply that it was time consuming to place complex logic trees. Since each condition needed to be an explicit node, you would often have massive trees of conditionals that were just as hard to parse as their old XML counterparts.

Which leads us into LD50. This entry is an interesting jumping point since the general premise of it is similar to that of LD45, but with newer tools and more experience.

With latest iteration of our narrative tool, the biggest goal was down to improving readability and ease of use. Simple logic can be placed directly inside links (highlighting them yellow). Logical blocks can be surrounded with comments to quickly identify what they do. And the contents of each node has been moved to the inspector/side bar, instead of on the tree itself. This moves a ton of visual clutter out of the tree, and makes it much easier to rapidly identify what nodes are doing and where you bug may lie.
Further, since this new tool is integrated directly into our engine, nodes can interact more directly with our engine. For example, dialogue nodes pop up a live preview of the in-game interface at the current line, so you can adjust the expression, line length, and pacing without needing to run the scene in the actual game.

There were also some under the hood changes like moving away from JSON, towards a custom format that survived git merges better, and changing how the engine interacted with nodes such that we didn't need giant piles of macros to keep things type safe.
Are we at the perfect solution yet? probably not. LD50 has once again highlighted issues that need improved. For example, we need a way to visualize the how the game will execute through the various game states, as a common bug we hit during development was when none of the conditions were met, and the game just soft locked. But we already have a solution in mind, so it's just a matter of iterating and trying again next LD.
With each game we learn something new about our workflow, and find more ways to optimize it. As a benchmark of progress, between LD45 and LD50, our script grew over 3x in length, which implies I've either gotten way faster at typing, or the latest iteration of our tools have just enabled me to be that much more efficient.
Hopefully this look into how we write our games has sparked some ideas within you. I'm not about to assert my way is the best way, or even a good way, but it's certainly not the worst way we've done things, and hopefully it'll help you add something cool to your next LD entry.
Thanks!