How I wrote an application to help me write a screenplay
Programming and writing are my two passions, although in different measures depending on my life state. I started writing a screenplay a while back but progress stalled. At the same time, my drive to program had been flaring up so what would have been better than to program a tool that supports my writing ambitions!
Thus, the “Scene Mover” was born. This application allows me to compose a screenplay out of scenes: Instead of writing and adding scenes to one single document, this tool compiles a final script out of multiple files, one per scene. Using drag and drop, I can reorder scenes which should help tremendously when writing my screenplay in a non-chronological order where I would want to concentrate on small chunks individually.
Using this tool, I can have a folder /scenes
in my project which holds different files for different scenes. The extension assembles a full script out of the individual scenes and recompiles the script every time something changes in the directory, either because a scene is updated, a scene is added or because scenes are reordered using the drag and drop feature.
A short recording of the rearranging functionality:
Technical Implementation
Backend
My initial idea was to create a Visual Studio Code extension that would allow me to bring up a new tab right in my editor that shows the application. This idea arose because I use Visual Studio Code as my writing tool of choice along with the fantastic screenwriting extension betterfountain (to which I have also contributed a small number of features over the past years).
It was clear to me that I needed to render the app in Visual Studio Code using a webview. Webviews in VS Code can receive events from the outside as well as send events to the outside. On rearranging a scene using drag and drop, the webview could notify the rest of the extension that a scene has moved and it could then take care of rearranging the files.
But since visual studio code extensions are written in JavaScript/TypeScript, using this approach, I would have had to write code to assemble the final script and to move files in nodejs. Although JavaScript is a big love of mine, I wanted to write a lot of this application in Golang as I believe in its processing speed and as I find working with the language extremely satisfying and joyful. While I could have written several small CLI tools in Go that could then be started from within nodejs using child processes, I decided to create a local webserver instead. Although this decision forces users to always have a webserver running locally, it helped me tremendously with writing this tool as I am quite familiar with web apps and as it meant I could easily test the file moving and script reassembling functionalities. Additionally, it was clear to me from day one that this project would be specifically written just for myself - thus, anything goes.
Although I am calling this backend application a server, the program actually does a little bit more: aside from the webserver responsible for reordering scenes, the program also launches a file watcher that looks out for any changes to the scenes/
directory and recompiles the full script. The webserver itself exposes two endpoints: GET /scenes
which lists all of the scenes so that the web interface can display them, as well as POST /scenes/transition
which is called by the web app to reorder scenes when the user reorders them using drag and drop.
For POST /scenes/transition
, the user needs to supply a JSON body with two parameters: the name of the file that is about to be moved as well as the new position of the scene. Changing the position of an item in a slice might not seem like much, but I found it to be quite the challenge. What I ended up doing was what any respectable programmer would do: steal someone else’s genius and reap the rewards! I discovered a JavaScript library that did exactly what I wanted, copied the algorithm and translated it into Golang. Since Golang does not have an equivalent of JavaScript’s splice method, I also additionally stole - um, relocated - an implementation of the splice
method in Golang.
Frontend
As outlined previously, the original goal of the project was to spawn a webview from within VSCode. Thus, I did some research and noticed the existing VSCode extension sheepy-fp-guilde that spawns a React app within a webview. I used this extension as a starting point but tore out all of its pieces except for the foundations.
Choosing React was a no-brainer to me as I have extensive experience with it. But since I am not a frontend developer by profession, I find it usually quite the surprise coming back: every time I revisit the framework, new programming paradigms, toolchains and API methods come up; in my case most notably memoisation and Redux Toolkit. I found it equally challenging as well as rewarding to learn about these.
Aside from React, I use Redux to handle state changes and Sass to create my (minimal) stylesheets. The main chunk of the project is the drag and drop feature which is powered by React DnD. Getting this to work was by far the biggest challenge of the entire project. The tutorial for React DnD is very detailed which caused me to lose focus. I ended up relying very little on the tutorial and instead experimented for a while with the various functionalities, fueled by spot-reading of the documentation. But alas, once the drag and drop worked… it was incredible! I jumped out of my chair, clenched my fists and proclaimed “It works! It works!”
View into the future and summary
What helped me tremendously with this project was my mindset of imperfectionism. Instead of trying to create something impressive which could be used by others, I focused on getting things to work for myself using the tools I already know. Otherwise, I might not have been able to finish the project at all.
I know that there would be plenty of things to improve on this app, but that’s the beauty - I don’t care! For example, the app currently cannot be rendered in a Visual Studio Code webview, instead I need to open it in a browser. But despite this, it works and does what I wanted it to do; I am now able to rearrange scenes and write my screenplay in a more compositional manner. Therefore, I should focus on what I actually built this for: writing my screenplay!
Backend code for the project on GitHub
Frotend code for the project on GitHub
Original screenplay image from rrfedu.com, modified