v.1.0.0: Making of the jam edition



Ahoy merchants!
I’m excited to announce the release of Lacus Opportunitas, a lunar lake trading simulator developed for Games for Blind Gamers 4. In it, you explore a lake, trade commodities between its ports, and perform tricks along your way. Combining my love for ambient music, immersive exploration, and aquatic environments, it’s truly an experience that’s uniquely shiftBacktick!
Many thanks to the folks who made this jam possible! Continue reading to learn more about this project, from its initial design concept to release. In the meantime, I’m eager to play and review all of the other submissions. May the best project win!
Initial concept
This project is dedicated to the Redditor whose wish that I had participated last year did not come true. Fulfilling their wish was my biggest motivator to find a fun concept and get it over the finish line.
Leading up to the jam, I explored a litany of disparate concepts that didn’t quite feel right. The furthest along was a competitive online game, which was so developed that I had prototyped its entire API and database backend with Symfony framework. However, like the other concepts that went into the recycling bin, it became apparent that it was too much work and expense for a month-long jam.
Over the winter holiday, I had the opportunity to leave the country and go somewhere tropical for a week. It was very inspiring, and thankfully I had brought my notebook to let my game design thoughts wander! What inspired this specific concept was a gorgeous day that found our squad chartering a yacht. My best memory from the trip was sitting on the bow of the ship: the warmth of the sun against my skin! the salty breeze sweeping through my hair! the rushing cacophony of the open water! and everything in motion so picturesque and glimmering!
I wanted to capture that exact feeling! So within my notebook sprung a lake where it’s eternal. A list of prefixes and suffixes generated random place names. Their economies relied on each other. Some sold unique goods. You would sail from place to place to enrich everyone. And wouldn’t it be great if there were huge waves to jump and tricks to perform for bonus points?
Development process
Lacus Opportunitas was the fruit of that inspiration.
It was also my first project since Chimera that I didn’t document every step of the way. In previous jams, I’ve found a lot of joy in capturing my incremental progress and receiving early feedback. However, for this jam, I was unsure whether I could work on it consistently enough to journal with a similar cadence, hence this lengthy entry instead. Plus, I enjoy a good story!
My general approach for this project was structing it into a handful of phases, which would steward it from a thin vertical slice into its full intended experience. Let’s dive into their unique challenges!
The first weekend
I kicked everything off by cloning my syngen template repository at the v2.x.x
branch.
Then its initial Let there be light!
commit renamed it to Lacus Opportunitas.
The foundation
My objective for the first phase was to get players exploring the lake and docking at its ports. They should be able to do that with a basic user interface, minimalist graphics, and screen reader support.
Getting there involved cannibalizing past projects and stitching them into a transformative work. Although the interface itself dates all the way back to soundStrider, I tend to iterate with each project, so this code specifically came from Project Ephemera. All of my recent projects share a bloodline with Periphery Synthetic, who provides the boilerplate for the camera and graphical systems. And many of the recent improvements to the template repository, like input handling or autosaving, come from Fishyphus!
The dream from the notebook was built upon this amalgamation of a skeleton. From the main menu, the player could adjust their settings and start a new game. Each lake was procedurally generated from a seed which dictated the number, names, and placements of its ports. From the dock screen, the player could undock to explore the lake. When they approached another port, they would dock with it automatically.
The first screenshot
By the end of the first day, I captured this early screenshot of its evolving graphical style:

While undocked, the player is greeted with these abstract, colorful, pointillist graphics similar to Periphery Synthetic. A concert of shaders helped elicit that lunar lake setting, from a colorful sky of blues and pinks in the background, to the moving points of light on the surface in the foreground. The most difficult part: aligning and shading the Earth eclipsing the sun above the horizon, reminding me that there is still much to learn!
Without seeing this image, a targeting system was added to make exploration accessible to screen readers.
In this iteration, it would automatically announce whatever the player was facing within an aria-live
region.
Future iterations would add targeting controls, and reduce how chatty it was with screen readers.
Implementing the core game loop
My objective for the second phase was to make it an actual game. The player should start with money at a port, buy goods, undock and explore the lake, dock at another port, and sell those goods for profit. They should be able to easily do that with an interactive tutorial.
Defining the economy
The five economy types were designed to provide multiple routes to success. The simplest trade routes took essentials from agricultural ports and traded them for waste at any other port. More complex routes arose from leveling up goods from one port to another, such as turning iron ore into iron bars to manufacture consumer goods.
For my own sanity during the jam, I found simplicity in the actual goods that made up the economy. Essentials stood in as replacements for separate foods, water, clothing, or medical supplies. Extraction, refinement, and manufacturing goods were similarly reduced into basic concepts. The general philosophy was that it should take about eight transactions to acquire enough credits for what might be considered the next tier.
The main exceptions were the luxury goods, which were unique to each luxury port around the lake. Their mechanics were largely inspired by one of my favorite games, Elite: Dangerous, which has similar goods that become more expensive as they get farther from their origins. These add a bit of spice which could shake up a more standard route.
Trading its goods
Screens for buying and selling goods were added to finally make these economies interactive.
A lot of thought went into optimizing them for screen readers.
Importantly, they had aria-live
regions which politely announce changes to your inventory and credits as transactions are made.
They also had a dynamic sort order, which prioritized the goods you could buy or sell to make the most profits.
One challenge I faced with these screens was Chromium bug 356548151.
The interface uses tabindex="0"
on table row elements to make them clickable.
However, this bug prevents screen readers from accessing the contents of the table row.
The workaround was to generate an aria-label
attribute for each row which mirrors their content.
It’s clumsy, but this breakthrough will allow me to adopt a more recent version of Electron in future releases.
A late addition in this phase was the fast travel screen. Initially, it was a tool for debugging, but soon became a necessary core feature. Once a sufficient number of ports were discovered, this made the game playable through the menu alone, significantly increasing its pace. Future iterations added incentives to limit this behavior, such as time advancing much faster than traveling by hand.
To tie it together, a tutorial screen was created to onboard players with new gameplay information. Whenever the screen manager was about to transition to a new screen, it asked if the upcoming screen has any tutorials. If a tutorial was available, then the tutorial screen would intercept the screen transition to display its text. They were fun exercises in worldbuilding that introduced their concepts through the lens of the lunar lake!
Immersing with synthesis
My objective for the third phase was to transform it into an immersive audio experience with real-time synthesis. The player should be able to turn off the graphics and navigate the lake with audio alone.
Synthesizing the lake
The environmental soundscape took inspiration from the aquatic environments of Periphery Synthetic and Fishyphus. Specifically, they all leveraged layers of filtered pink and brown noise to evoke the sounds of the open water. I’ve always found that a bed of dynamic wind can work wonders to make synthetic environments more believable, and this was no exception!
Importantly, five synths were responsible for communicating the size and distance of nearby waves. This number originated from S.E.A., and later Periphery Synthetic, which shared a similar need. Starting from straight ahead, each were panned in 72° increments around the listener. It’s the perfect balance between audio performance and spatial detail, with four too limiting, yet six too demanding.
My biggest challenge with this phase was representing the ports themselves. I wanted each port to emit a unique droning sound, yet together they should comprise an evolving musical piece like Music for Airports. For simplicity, I reached for a major pentatonic scale for its general consonance, and prime numbers to maximize the rhythmic complexity of the piece. When positioned diegetically and played together, they sang this flowing chord which ebbed and flowed as you transited the lake.
Importantly, they were built to react to the targeting system. When directly ahead, they emitted a quick ping like a point on a compass. When targeted, their parameters swiftly shifted into a more present directional sound that aided navigation. A complementary chord strummed on and plucked off as the effect was toggled, always related to their root notes. It was all musical!
Synthesizing the interface
When docked, the environmental sounds would cede to a sparser ambient soundscape. The port’s root note, along with that complementary chord, were again conducted by prime numbers. Oscillating in amplitude, color, and position, they formed unique musical systems for each port.
This was especially fun when quickly switching between ports with the fast travel screen. In this phase, every screen transition and click was complemented by a random musical stab. The solo that each interaction plucked had a delightful movement against the changing chords. In a way, the menus had become an instrument to play themselves.
The most interesting time sink during this phase was implementing the title music. Originally, it was a minimalist piece that I started writing for an elaborate side project. I had a recent mixdown, but had lost the source files in a hard drive crash. A more trained ear could do it quicker, but over the course of six hours I transcribed it into an array of MIDI note numbers. Then I built a synthesizer to step through the sequence with some automated parameters on the splash screen.
The theme was slowed to a crawl when transitioning to the main menu. Processing it with convolution reverb to produce a piano-like sound was a fun rediscovery for me! Overall, I love how recontextualizing it in this way led to different appreciations of the same composition. In future iterations, the main theme was also reused to provide melodies for various synths.
Prioritizing fun
For the final phase, I had to make sense of an unfocused list of ideas to improve the overall game. Ideally they should incentivize the player to undock and travel the lake between each transaction. During this phase, the project also became more focused with a smattering of polish.
Leveling things up
One of my challenges with balancing the economy was accepting the inevitability that certain ports and goods would become less desirable in the later game due to their opportunity costs. For example: why visit an agricultural port at all after the early game? A leveling system was added to ports to address this problem. It kept track of all transactions at a port, leveling them up at an exponential rate. The level applied a bonus to their buy and sell costs to extend the shelf life of cheaper goods.
Similarly, the low cargo limit becomes quite cumbersome in the later game, limiting the potential choices that can be made. For example: why buy any waste if you can fill your cargo with four units of agricultural equipment? To further reward players for leveling up each port, the cargo limit was made to increase with the average level of the lake.
Deliberately, these bonuses had no limits. Philosophically, it was important to me to reward players for their precious time. Besides, I doubt anyone has the patience to raise anything beyond the fifth level anyway!
Credits in bottles
To solve the fast travel paradox, I began focusing exclusively on items that would incentivize exploration of the lake. The simplest was adding collectable bottles which reward credits when recovered from the lake.
The bottles were managed by a spawner which prioritized their discovery by the player. The spawner would always place them ahead of the player’s forward direction. If the bottle was missed, then it was repositioned in an easily findable place within audible range.
To prevent farming of the bottles, they were limited to only three spawns per excursion. The number of collections were then preserved between saving and reloading to prevent further save scumming. The player might still dock and undock to reset the bottles, but they would lose profits by doing so.
To continually incentivize their collection, their rewards were scaled with the player’s progress. Their net worth was calculated to determine the most expensive good they can purchase. This amount was then divided between the three bottles per excursion. Overall, they offer a fair mechanism for growing profits, even with the most inefficient routes.
Jumps and tricks
My goal for the last weekend of the jam was to push out one final wishlist feature: performing sick tricks for points!
As a lifelong PC gamer, I never had the pleasure to grow up with the Tony Hawk Pro Skater games like my childhood friends. My experience with those titles involved watching an awesome run, being handed the controller for my turn, and aimlessly mashing buttons without any understanding whatsoever. In later life, I recognize it as more of a quirk of how my brain works: the same part that can’t tell left from right is also the one who fumbles to their death in a FromSoftware game. Therefore, my vision for the tricks was to lean into this button mashing by simply rewarding players for their creativity.
Anyway, I first had to get players off the ground.
Every single project of mine has a clunky movement system, so I actively sought a new approach for this one.
I ended up keeping track of a z
-value that was accelerated as the elevation changed.
If it changed too quickly in a negative direction, then the player was launched from the ground at that velocity.
While airborne, they were gently returned to the surface at approximately half gravity.
While implementing this, I explored different sounds that could indicate leaving the surface. After a few iterations of things that raised and lowered with pitch over altitude, I realized that the answer was already in front of me! In the end, the engine sound needed to change pitch as it exited the water, continuing to pitch up and down as it soared through some parabola in the air.
The tricks themselves required a bunch of input mapping that would drive a simple state machine. Whenever a trick button was pressed, it would control a virtual instrument assigned to that specific trick. The melodies for each trick were selected from the main theme, and randomized with each trick run, to give players a way to inspect and interact with it further.
Bonus credits were rewarded to players at the end of each run of tricks. The formula added together the longest duration of each trick with the total times they were triggered, multiplied by a factor to encourage equal use of the tricks. To limit farming at early levels, their rewards were multiplied by the average port level, incentivizing a balance between work and play.
Next steps
Ultimately, I’m quite pleased with how well this project materialized from dream to reality in just one month. But what of its future?
That mostly depends on how it’s received during the jam rating period and what feedback is actionable. In many ways, it’s a complete and relatively bug-free package. However, there are many opportunities to expand it, such as more types of goods and economies, or introducing a non-circular lake. Their implementation would need balanced against my burning desire to finish the first expansion for Periphery Synthetic.
Something else I’m considering is bringing it to Steam as a free download. I’ve enjoyed expanding my catalog there by offering Fishyphus and Wurmus for free, and I feel that this could make another well-polished addition. My biggest challenge with Steam is always the trailer and sheer number of graphical assets needed to publish on the store. It’s a lot! I’ll share more details if that gets closer to reality.
Thanks for reading. Enjoy!
Files
Get Lacus Opportunitas
Lacus Opportunitas
Lunar lake trading simulator
Status | Released |
Author | shiftBacktick |
Genre | Simulation |
Tags | Abstract, Atmospheric, Exploration, Open World, Procedural Generation, psychedelic, Relaxing, Sailing, Skating, Trading |
Languages | English |
Accessibility | Color-blind friendly, High-contrast, Interactive tutorial, Blind friendly |
More posts
- v1.0.1: Division-by-zero hotfix8 hours ago
Leave a comment
Log in with itch.io to leave a comment.