Title Screen

Riverborn

A game about canoeing on a tropical river.

You have 5 minutes to get photos of 3 river animals. Some Aussie guy is very insistent about this.

Where is the river? Are all these creatures authentic river fauna? Is this a canoe or a kayak?

Launching

The easiest way to run the game is to install uv and uv run riverborn.

Controls

At the title screen, press Space to play.

  • Left Arrow - paddle on the left side
  • Right Arrow - paddle on the right side

That's it! Go spot some fauna! Quick, because your mate said so.

When you're done

There are some editor controls you can play with. They are all invoked using the mouse.

Press Tab to cycle tools (the CLI says which tool is selected). Click/drag to use the tool. Mousewheel to cycle animals.

The terrain and animals are persisted when you quit the game. Don't mess up your copy of the game data and blame me.

github.com/lordmauve/riverborn

Awards


The "Mick Dundee" award for dodgy Australian accents in a voiceover.
Presented by ntoll

Rowrific
Presented by zwerver

The "it looks more like a worm to me" for not actually being a Sandworm of Arrakis
Presented by ntoll

Give this entry an award

Scores

Ratings (show detail)

Overall: 3.5
Fun: 2.7
Production: 4.2
Innovation: 3.7

40% respondents marked the game as not working.
Respondents: 6

Files

File Uploader Date
riverborn-1.0.4.zipfinal
Fix packaging problems
mauve 2025/03/30 23:51
riverborn-1.0.0.tar.gzfinal
Source Distribution
mauve 2025/03/30 01:08
screenshot_2025-03-29_235904.228189.png
Title Screen
mauve 2025/03/30 00:11
screenshot_2025-03-29_004136.868098.png
Tons of plants
mauve 2025/03/29 00:43
screenshot_2025-03-28_012803.501478.png
Shadows
mauve 2025/03/28 08:37
screenshot_2025-03-27_010145.381147.png
Instanced rendering
mauve 2025/03/27 01:04
screenshot_2025-03-26_084409.646413.png
Canoeing
mauve 2025/03/26 08:49
screenshot_2025-03-25_095707.888166.png
Drawing ripples
mauve 2025/03/25 09:57
Screenshot_from_2025-03-24_10-38-16.png
Water
mauve 2025/03/24 10:38
Screenshot_from_2025-03-23_23-47-48.png
Muddy water
mauve 2025/03/23 23:48
Screenshot_from_2025-03-23_17-31-02.png
Ripples
mauve 2025/03/23 17:31
Screenshot_from_2025-03-23_11-25-01.png
Terrain
mauve 2025/03/23 11:26

Diary Entries

Day 1

I got quite a bit of code out yesterday. I very quickly got a terrain demo up and running in pygfx:


Terrain in pygfx

But I wanted to write shaders and pygfx has an API for that but it's obscure and barely documented. ChatGPT is barely spitting out correct pygfx code - it doesn't know it very well. Nor do I. I found ChatGPT and Copilot are pretty good at spitting out moderngl + numpy code. All programming is now AI pair programming so I went with what my AI pair programmer was comfortable with and switched to moderngl.

The next thing I tried was to get o3-mini-high to produce a bunch of separate graphics demos that I can then refactor into a whole. First the classic 2D ripples algorithm. This was everywhere in the naughties but this version is on the GPU:

Ripples demo

This took a lot of debugging though: the AI didn't store a velocity component, and I manually added one but didn't spot some code that reallocated a texture to only have one component.

Then I got it to do a water plane with cube map and Fresnel. The input is a heightmap which should pair with the ripples code.

Water with reflection

After a lot of refactoring of the demo code I finally got the water and the terrain, with transparency based on screenspace depth. The AI got me in the ballpark but then started to offer changes at random, so I got it over the line manually.

1 comment

Day 2

I spent much of Day 2 refactoring to encapsulate the different effects better. With a wrapper around the ripple simulation I was able to combine it with the rendering shader, and hook up mouse input to draw ripples:




It looks pretty good in a screenshot but better in a video.

1 comment

It's a wrap

I'm happy to return to PyWeek after a few years where having little children made it seem impossible. I worked pretty intensely this week but also took a lot of time out to do family things, more than in PyWeeks of old, when I could spend the whole of the first Sunday and final Saturday programming.

AI is picking up a lot of the slack, and o1/o3 and Claude 3.7 are pretty incredible tools for writing game engine maths. But dumb too, particularly at project-wide scale. I got best results asking them to produce self-contained demos, bugfixing them, and working to integrate that into the whole. It's not quite vibe coding: it's maddening trying to unpick what they did and why it doesn't work in a different context.

AI totally changes the way we program and it's not realistic to put that genie back in the bottle. The skill for PyWeek is being able to collaborate effectively with AI, and I think I'm getting better at that, and leaning into it more.

I'm pretty happy with my game. It has the vibe I wanted: lush tropical vegetation, peaceful river, and the challenge of steering a canoe with one paddle while trying to do it quickly.

The original idea was more towards kayaking in a flowing river, and I did get o3-mini-high to produce a Navier-Stokes solver but I never tried running it: it was always a stretch goal and I while have some understanding of flow-mapping shaders, I first needed the environment in which to set that system.

A pivotal (and probably bad) decision was to switch from pygfx (high level wgpu-based) to moderngl (low-level OpenGL) plus moderngl_window (pretty low). I did this because I needed to write shaders for the water effects and pygfx has an undocumented shader API, while AI was doing a great job with moderngl. In principle moderngl_window has model loading and scene management, but it turns out it is much less complete than pygfx - it doesn't support the instanced rendering I needed for vegetation or the ability to customise the uniforms and attributes in order to do shadow mapping. In the end I wrote a lot, a lot of moderngl code, reimplementing moderngl_windows's OBJ loading and scene management in order to add instancing and shadows. I guess whichever way you go, a 10% gap means you end up rewriting 70% of the tool you're using.

Another reason I chose moderngl  was that I could pull in bits of Wasabi2D for UI, which afaict moderngl_window doesn't really do. But in the end that didn't happen. I had a brief look at what it would take and it's hard because Wasabi2D is pretty tightly coupled. However I used Wasabi2D for some coroutine code that made the paddle interaction easier to write (I wanted to make the paddle animation better, but alas, no time).

I hope you enjoy the game. Let me know if you have any issues running it!

1 comment