Progress Report - July 15, 2026 (Patreon)
Content
Hello, everyone!
I apologize for this week's Progress Report being a day late. I'll be perfectly honest, I just didn't have the executive function to make this post last night.
Lip-sync, shader experiments, and unfinished game-design notes - Let's get right into discussing what all got done over the past week, shall we?
So, right up top: Overbreed! Unfortunately, I haven't heard back from neither Milly nor Cinder for the next batch of audio. If I don't hear from them by end-of-day Friday, I intend to toss them both a message and just see what's what.
But! I did manage to get most of Milly's new Mercy audio lip-synced. Finishing up the last of it is one of my goals for today, actually. Specifically, I've gotten the first 260 seconds of Narrative B fully lip-synced, which is just over 80% of the 320-second total runtime of Narrative B.
I can't overstate how exciting it is for me to finally see these dialogue sequences fully spring to life, with full audio and lip-sync. It's taken damn near half a decade, but these scenes that I wrote out all those years ago finally being fully realized... it's really not an experience I can convey with words. You really have to experience something like it yourself to fully understand the feeling.
On the topic of Narrative B, this is also the only point in the entire video where we actually see Sombra's signature cloaking in action. And I ended up spending some time really experimenting with that.
I have long been extremely unhappy with how Sombra's cloak shader looked in Source Filmmaker. It's damn near unreadable, and the close-up of her licking her lips as she watches Mercy go for that second helping of three liters, you just genuinely cannot make out what you're looking at.
While looking up something completely unrelated on the Valve Developer Wiki, I just so happened to come across the cloak-shader parameters, and saw that they support light refraction. This surprised me, as the cloak parameters I have been using since Kasumi Goto's cloak in Miranda Versus The Galaxy certainly does not have any reference of refraction.
So I spent some time experimenting with it in Source, and it genuinely makes a huge difference.
The default refraction value in SFM seems to be around 0.1 (despite the Valve Developer Wiki saying the default value is 2 - it most certainly is not). That roughly gets the same as what you see above. When I set the refraction value to actually be 2, though...
Suddenly, the whole image comes sharply into focus. You can clearly see not just Sombra's face, but even the shape of her hair. You can see her eyes, her eyebrows, her nose, her tongue. Hell you can see the fold between her top lip and her nose.
All while still very much having that signature transparency of a sci-fi cloak.
I did some more experimenting with the values, and what I ended up with is a refraction of 0.25 for her outfit, and a refraction of 2.0 for her face.
This presents a nice blurry fuzziness to her outfit, obfuscating the details while maintaining the silhouette; while keeping her face and hair in sharper focus, for emotive clarity.
I also did some experiments with how exactly the cloak shader renders. Something I've noticed early on is that, in the Source Filmmaker viewport, the cloak shader seems to draw whatever the work camera is looking at. Which can make it difficult to get an accurate feel for how the cloak actually looks like.
I confirmed that, when you render out footage of the cloak shader, it ignores whatever the work camera is looking at. It will, instead, render as if the work camera and the output camera are the same. Which means, to get an accurate preview of what the cloak would look like in the viewport, you simply copy the output camera to the work camera (which has a dedicated button in SFM).
I also discovered that there is one exception to this behavior: render targets.
Render targets are a method for drawing the output of a camera (typically different from the output camera and the work camera) onto a texture. Half-Life 2 used them extensively for the Breencasts seen throughout the trainstation at the beginning of the game, and I similarly use them for having real-time footage playing on a screen.
Well, it turns out, if a scene has a render target, then the cloak shader will draw the render target, rather than whatever is behind the material.
This could be beneficial in some use-cases. For example, you could have a render target looking at a patterned texture, and then use that to have your cloak effect have a pattern applied to it.
But in my use-case, where I have what Mercy is sucking herself off to playing on a screen, as Sombra creeps on her, it, uhh... It's decidedly less useful.
So that's something I will have to figure out at a later time. The easiest solution will be to adjust the camera angles so that Sombra isn't blocking the screen, and then simply render twice - once with the render-target on, for Mercy's viewing pleasure (and pleasure viewing); and once with the render-target off, for Sombra's cloak to look correct. And then simply composite them together in post.
I'll probably end up going with that, just because it is so simple.
Moving on, next up is To Breed And Bond. As I am sure you all saw, Episode 3 was released to early-access this past Friday, with Episode 2 being released publicly. Episode 4 is currently chugging along. All of the dialogue has been lip-synced, as well as all of Pharah's non-dialogue audio. Most of Mercy's non-dialogue audio has been lip-synced, and finishing off the rest of hers is my goal for today, with a stretch goal of getting at least half of Symmetra's non-dialogue audio lip-synced as well. I want to have all of the lip-syncing done by end-of-day Friday.
I also ended up splitting some of the longer shots in the beginning of Episode 4 into shorter shots, which necessitated adding some new animation. Including the cock-slapping animation that is this post's preview image. I totally didn't do that because I realized I accidentally missed lip-syncing a whole line of dialogue, and didn't want to lip-sync it. No sir. That had nothing to do with it. Nothing at all.
With all of that in mind, I want to take a moment to step back and discuss the infrastructure around these releases a bit. Strap yourselves in, boys and girls. You're going to learn how the Internet works, whether you want to or not. Or at least, a small part of the Internet, and a small part of how it works.
Throughout the release of Love & Degeneracy Chapter 1, the public releases were plagued with issues of the website not updating properly for them. The only solution I've been able to offer them at the time was to hard-refresh the page, clearing the cache and forcing a complete redownload of the site's data file.
That did work, but it is a technical solution I can't expect most folks to easily figure out. And so I ended up spending some time this past week, following the public release of Episode 2, installing a more manageable solution.
What I ended up implementing is what's known as a cache-buster. Very simply, modern website caching works on URL names. When you visit a given URL, your browser will store a local copy of everything it downloaded (images, videos, and--relevant to this discussion--data files) and assign them to that URL. When you visit that same URL again, it will see if it has local copies, and if so, it will simply instantly load them, rather than download them again.
This works great for websites with static content. And for websites that are built by people who actually know what they're doing, and use server-side infrastructure to dynamically serve content as the website loads, it isn't an issue.
But the Series page isn't static, and I don't know what I'm doing. I have all of my data stored and served clientside, meaning that it all gets hit by the cache. Which means that, for people who visit the Series page more than once, they get stuck never seeing new content load into it.
Which is bad.
Luckily, there is a very simple way to get around browser caching. As I said above, caching is done by URL. So the force the browser to redownload cached content, you simply change the URL. And luckily, the way modern browsers work, that is very simple.
If you've ever copied a YouTube URL, you have probably noticed these little symbols in the URL.
These are URL parameters, and are a way to provide the website with additional information, beyond just the location of the page itself to load. You can set these to anything you want, and it won't change the page being loaded (though it will likely change how the page's code behaves). And, importantly, these parameters can be named anything.
Unless a page is coded in an unnecessarily strict way, it will simply ignore any parameters it isn't expecting. You can add a "&peepee=poopoo" to the end of this URL, and the page will never give you up, nor will it let you down.
And conveniently for us, those parameters are considered part of the URL. And as such, they are included in the caching system. More to the point yet - if you change those URL parameters, they change where the cache system looks.
And if you add a different URL parameter every single time you load the page, you guarantee that you will never hit the cache. Meaning that fresh content will be downloaded every time.
This is the core premise behind cache-busting. The exact process of how you cache-bust differs from implementation to implementation, but the process I went with is probably the simplest: simply tagging the current time, down to the second, onto the end of the URL as an unused "time" parameter.
To be more precise, I don't actually cache-bust the Series page itself. If you load the Series page up right now, you won't see any random gibberish numbers in it. At worst, you'll see the name of the current video you're looking at, which I use for quickly linking to the specific videos on their release.
Instead, what I cache-bust is the data-file that the website pulls off my fileserver, for dynamically generating the webpage. I store all of the video information in a data file that the webpage downloads and reads, rather than the right way of storing it all on a database that the server queries and delivers the results of to the webpage. Again, as stated earlier, I don't know what I'm doing.
This data file is perfectly exposed to the public, and it has a URL just like anything else on the web. You can visit it right now, if you wanted to. It won't mean much to you, and if you've visited the Series page before, you already have a copy of it on your computer. But you can do it.
And because it has a URL just like anything else on the web, it gets cached just like anything else on the web. Which means, whenever I push changes to it, people who have visited the Series page previously won't see those changes, until their browsers decide to clear the cache for the data file.
Unless, of course, we cache-bust it.
For all of this bluff and bluster, the actual code to cache-bust it is honestly disgustingly simple:
It is genuinely just a one-line implementation. Just injecting the current time directly into the URL for the data file, specifically as a "?t=" parameter. Again, this parameter isn't actually used by the page. It doesn't do anything. Except make the URL unique.
Which, in turn, means that every time anyone loads the Series page, they will always get the most up-to-date version of the data file. Meaning they should always see the new releases, as soon as they become available.
In theory, anyways. I haven't actually extensively tested this, because testing caches is actually really hard to do. They're kinda built to be transparent. You're not meant to see them. So testing them is a challenge.
Now, those who are more familiar with web development may have noticed a potential complication in this solution: if URLs getting cached is causing updated versions of files to not be downloaded, and the solution involved updating the Javascript to implement a cache-buster, then wouldn't the fact that the Javascript is itself a URL file that gets downloaded mean that the very fix may not get downloaded due to caching?
And you'd be correct! That is why I also had to implement a cache-buster on the HTML page itself, to force it to always download the most recent version of the Javascript file, so it can in turn use the updated cache-buster to download the most recent version of the data file.
This is why I stick to developing offline tools.
And then finally, the last thing I want to briefly mention for this week's Progress Report is Starsail Bordello.
After spending more time last Sunday to continue fleshing out my notes, they eventually got to the point where I felt like a simple Notepad file is becoming too cluttered and messy to keep track of everything.
So I've spent some time over the past few days to move my notes over to a proper Obsidian vault. This serves three main purposes:
Firstly, it allows me to properly hyperlink pages together. So many of the systems in Starsail are related to each other, and in the raw Notepad file, that means constantly scrolling up and down through my notes as I jump from topic to topic. With Obsidian's wiki-esque hyperlinking, that is much easier to navigate. It just makes keeping my notes organized easier.
Secondly, it gives me the space to properly expand and explain the ideas, rather than the more terse form that my internal notes take.
And then thirdly, it gives me a way to easily export and share the notes with other people. My ultimate goal is to publish the entire vault to the public, once the design has been finished, and use it both as a way to pitch the game to people who may be interested in watching its development on livestream, and as a public account of the game design. With the vault being online, it means anyone can follow along with the development, and see exactly where things are going.
With that being said, transferring all of my notes over into a properly readable and interlinked medium has been slow-going. It turns out, I can write internal notes meant only for me a whole lot easier than I can write a proper Wiki. But it'll all be worth it, in the end.
My goals are to continue tinkering with writing up all of the existing notes over the week. For this Sunday, I am going to stream some more work on Primordium Episode 4, simply because I haven't streamed for the past 2 weeks and I want to stream at least semi-regularly still. Ahead of this Sunday, I intend to publicly post whatever the current state of the Obsidian vault happens to be in, to at least give people some material to chew through, as incomplete as it may be.
Toward that end, I am offering you all right now an even earlier peek of that Obsidian vault. I am also providing my raw Notepad notes as well. Do be aware that the Notepad notes are still incomplete, there is a lot more design to be figured out yet. But they are more complete than the Obsidian vault.
So if you find yourself intrigued by the Obsidian vault, and want to read more of my design, and are brave enough to tackle my raw notes, they are available to you.
The final note I want to leave on this subject is that, I swear this is a porn game. It may not look like it, after realizing there is a whole-ass disambiguity page for the FTL tech in the game, along with an entire instruction manual of the different mechanisms of the FTL system. But I swear, that really is only a minor component of the overall game!
With that being said, that's all for now, everyone! Until next week!