Plague: a Godot Wild Jam 18 game postmortem


A few weeks ago, Jay Cano and I took part in the week-long Godot Wild Jam 18 and submitted Plague. Here are some of my personal thoughts on how we did and maybe some lessons for the future!

๐Ÿ“ˆ Results

Out of 43 submissions, we ranked 6th overall. That is the top 13th percentile. More importantly, we ranked 3th for fun or top 6th percentile. For the sake of comparison, Onederful Wizard, which we submitted to the GMTK 2019, ranked 294 out of 2617 entries (top 11th percentile).

Even though both games received positive feedback, Plague ranked high for fun. We’re very happy about that!


๐Ÿ‘ What went well

โœ… Splitting tasks

During the previous jam, Jay and I rushed to implement features like crazy. Despite, Godot being very source control friendly, we ended up having to manually resolve too many merge conflicts.

This time we were much better organised and split tasks in a more consistent way.

  • ๐Ÿ‘‹ David: player movement, enemy behaviour, individual game systems, menus and effects.
  • ๐Ÿ‘‹ Jay: procedural level generation, level section design, enemy/item spawning and putting it all together.

Additionally, we both had separate “playground” scenes to test our work. We barely faced any merge conflicts!

โœ… Platforming

We got pretty good feedback for tight controls. Our player movement is not too complex. For maximum control, we use a KinematicBody rather than a RigidBody. We did a direct implementation of the formulas explained in the Building a Better Jump GDC talk.

The proposal of the video is simple. The variables designers tweak to control character movement should not be gravity, acceleration, etc. They should be jump_height, jump_distance, time_to_jump_peak, time_to_max_speed, etc. It’s much easier to reason about the distance a player can jump to build a level than the combination of gravity and other physics variables.

Additionally, we implemented a very simple Coyote Time. This gives players a grace period to jump even if the character has left a platform. Tiny thing, but it makes the experience a lot more responsive.

โœ… Procedural level generation

The game is a procedurally-generated infinite side-scroller. However, we do not procedurally place tiles. We have 8 hand crafted chunks. Then, the InfiniteLevelManager script is responsible for randomly selecting the next chunk as the player approaches the end of the current chunk. Finally, it populates the chunk with enemies and ammo pickups.

level chunk

This gave us an infinite scroller with a very simple procedural generation algorithm and super tight control over the end result. We even managed to progressively increase difficulty by spawning more and tougher enemies.

With more time, we would have increased the variation and potentially procedurally place decoration on top of the blocks instead of having it built-in.

โœ… Re-using assets

Jay and I are both engineers and not very good at art. It was a wise decision to leverage multiple asset packs. This led to good feedback on both audio and visuals.

There are so many super high quality free assets, it’s insane. If your jam allows it and you have a similar skillset to ours, go for it!

โœ… Time for polish

We dedicated a big chunk of the last day for polish and juiciness, which massively increased the game’s perceived quality.

  • ๐ŸŽฅ Camera shake for damage and bullet impacts.
  • ๐Ÿฆ˜ Coyote time for jumps.
  • ๐ŸŽฎ Gamepad controls. The game detects Xbox/PS4 controller or keyboard and shows different instructions in the main menu.
  • โ›ˆ๏ธ Weather effects. We added rain, lightning and thunder that start at random intervals.
  • ๐ŸŒŸ Particles and SFX for everything that happens in the game.

โœ… Automatic deployment

Right before the jam, I implemented a Continuous Delivery pipeline that exports the game and publishes to on every push to master. This completely removed the panic that typically happens when something goes wrong with game export right before the deadline.

You can also go back in time and see how the game looked at any point in time during development.

๐Ÿ‘Ž What went wrong

โŒ Poor use of theme

Due to some issues with the organisation, the theme announcement was delayed by half a day. Knowing that we weren’t going to have much time to work on the game during the week, we started right away without a theme.

Unfortunately, we failed to go back to the theme and really incorporate it into the game. In the end, you can interpret that the monsters are the “plague”. Other games like Harvestor-3000 and Ceefax made a much better use of the theme.

By the way, this is not a complaint about the organisation at all. We really appreciate their efforts to keep the jam going!

โŒ Lack of end goal

The infinite platformer concept made people feel a lack of purpose. We didn’t save top scores, so there was obvious sense of improvement nor progression.

An alternative may have been to introduce a final boss and an end-game after a fixed number of chunks. Lack of time!

โŒ Yet another platformer

The game is a simple platformer and the procedural generation was not enough to make it fresh. That is why we scored the lowest in originality, ranking 28th or 65th percentile.

In future jams, we should prioritize a differentiating feature and really go with it. Hopefully, foundational things like getting movement right will take less time going forward.

โœŒ๏ธ Thanks!

I had a blast participating in the jam. Thank you for reading this postmortem and thanks to everyone who played the game and gave us feedback.

Continuous Delivery pipeline for Godot and

During the recent Godot Wild Jam 18, I implemented a full Continuous Delivery system to automatically publish Plague, our entry, on The pipeline relies on Github Actions, the same mechanism I use to build and deploy this very blog.

Setting up your own is easy peasy and possibly free!

โš’๏ธ Pipeline overview

  1. Someone pushes to the master branch on the remote repo.
  2. The game is exported for all available platforms. In this case: Windows, Linux, MacOS and HTML5.
  3. A new Github release is created with attached files, one per platform.
  4. The release artifacts are pushed as new versions of the game to


๐Ÿค– Configure Godot exports

First, you need to setup the export settings in your Godot project. The official exporting guide explains it perfectly. The thing to keep in mind here is that the preset name will determine the name of the published artifact. For example, a preset called plague-mac will result in a release with an attached file called

In the case of our jam entry, we targetted MacOS, Windows, Linux and HTML5.


๐Ÿ“ฆ Export the game

Create a .github/workflows/export_game.yaml file in your repo. We will tell our new action to run on every push to master. We don’t want new releases on commits that do not actually change the game. Our Godot project lives in the game folder, so we tell the action to only run if there are changes in that folder.

      - master
      - 'game/**'

For the actual export step, we will leverage the Godot Export Github action. The explains its usage very well.

Here’s our config in case it helps you. We setup the base version to be published, the links to download the Godot headless executable as well as the templates and tell it our project is located in the game folder in the repo. Whenever you interact with the same repo the action is being triggered from, you can use the secrets.GITHUB_TOKEN, which is automatically populated.

    runs-on: ubuntu-latest
    name: Export game
    - name: checkout
      uses: actions/checkout@v2.0.0
    - name: export
      uses: firebelley/godot-export@v1.1.0
        base_version: 0.1.0
        godot_template_version: 3.2.stable
        relative_project_path: ./game
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Whenever you push these changes to your repo, you should already start seeing jobs being triggered and releases being published.


๐Ÿšข Ship the game

Butler is the CLI, which lets you upload builds of your game. Our publish Github action will need to talk to using a valid API key. In order to get one, install butler and generate a key via butler login. Refer to the docs for the details.

Then, copy your Butler API key and go to and create a BUTLER_CREDENTIALS entry with the contents of the key.

Now create a .github/workflows/publish_game.yaml file in your repo. This action will run every time a new release is created or edited.

    types: [created, edited]

Our workflow needs to contain a job per platform we want to publish. Each job will leverage the Fetch GH Release Asset and Butler Push actions. We first download the latest release artifact for the platform we want to export and then we call the butler action to push the build to When pushing multiple times to the windows channel, each will be considered a new version of the same “product.

    runs-on: ubuntu-latest
      - name: download widows
        uses: dsaltares/fetch-gh-release-asset@master
          GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
      - name: release windows
        uses: josephbmanley/butler-publish-itchio-action@master
          CHANNEL: windows
          ITCH_GAME: plague
          ITCH_USER: dsaltares

That’s it! Once you push the publish workflow and make a change to the game, Github will export, create a new release and update your game on


On your game page, you should see something like:


โœ… Advantages

  • โณ Automated. Do not waste time exporting your game regularly and updating various stores.
  • ๐Ÿš€ Fast. Exporting and publishing for a jam sized game takes only 2 minutes.
  • ๐ŸŽฅ Restrospect. Check out how your game looked at an arbitrary point in the past without opening the editor.
  • ๐Ÿ’ฐ Cost effective. You get unlimited GH Actions minutes for open source projects and 3000 minutes for the Pro plan. That could easily equal 600 builds a month!
  • ๐Ÿคธ Flexible. No need to publish on every push, you can change the triggers to publish on every push to a release branch or simply on a fixed schedule.
  • ๐Ÿ”ญ Extensible. Add jobs to publish to other stores or platforms.

It would be amazing if you let me know your thoughts on this solution. Maybe you have a much better automated process or ideas on how to improve this one!

Plague - Godot Wild Jam 18


My friend Jay and I recently took part in Godot Wild Jam, a week long game jam where entries must be made using Godot Engine.

We had an absolute blast! The result is Plague, a procedurally generated action platformer. You can play it in the browser and use either a gamepad or keyboard!

.Get "linkText"

Don’t congratulate us on the visuals just yet, we used the Rogue Noir asset pack. However, you can check out our messy code on Github.

The main theme of the Jam was “Plague” although there were three aditional optional wildcards:

  • ๐Ÿ•บ 80s Baby! - Recreate the visual style of 1980s media.
  • ๐ŸŽฒ Take a chance on me - Create a system of chance in your game.
  • โ˜” I wish it would rain - Implement weather effects.

Even though many aspects of the game could be improved, we’re quite satisfied with the outcome. Especially because we had to manage day jobs and other compromises alongside the jam. Hopefully, I will get around to write a full post-mortem after the voting period ends and the final results come out.

In the meantime, give it a go and let us know what you think!

FOSDEM 2020 - Survival Guide


This weekend FOSDEM 2020 will take place in Brussels. FOSDEM is a yearly conference about open source software. It’s totally free and no registration is required. As a consequence, it’s massive and chaotic, but also a lot of fun! I attended in 2017 and loved it, so I really look forward to this year’s edition.

If this is your first time, here’s a few tips that may come in handy.

Attending talks

FOSDEM packs over 800 sessions in almost 40 rooms spanning multiple buildings of the Solbosch Campus (Universitรฉ Libre de Bruxelles). It’s impossible to attend everything and it’s advisable to come prepared.

I encourage you to download the companion app and build your own schedule. You can bookmark all the sessions you may be interested in and the app will nicely highlight conflicts.

Quite frequently, you get a very popular talk in a very small room. Room capacity is strictly monitored for safety reasons. Full signs will be posted othe door. With 8000 attendees, it may get tricky. I recommend arriving to the room for the session that precedes the one you absolutely don’t want to misss. Worst case scenario, you can stream any session live.

There’s always interesting people and conversations outside the rooms. I typically get very anxious about socialising in this context. However, it’s something I want to work on. I hardly ever regret getting over the initial awkwardness.

Transport and accommodation

The Solbosch Campus is not in the center, 1h walk from Grand Place. It’s relatively well connected with a couple buses and trams going there. With so many people, public transport and Uber will get busy. Be careful if you have to rush off right after closing!


A good area to stay is between the center and the venue. A good compromise that will make it easy to move around. Accommodation in Brussels is not super cheap by default. With so many people attending, prices will rise. Make sure to book in advance!


The center of Brussels is packed with places to eat of all price ranges. However, it’s a different story at the venue. You can choose between the cafeteria or the food trucks outside. Both will be crowded and won’t have great variety. At least, the cafeteria staff are quite fast.

Alternatively, there’s a couple of places you can grab a quick bite walking distance from the campus.

Satellite events

There are quite a few events organised around the FOSDEM weekend. Make sure to check them out in case you want to adjust your travel dates. For example, the Godot community is organising a couple.



Brussels is a great city. If you’ve never visited, it would be a shame to spend all of your time at the conference. Make sure to check it out!

Grand Place


Belgium is world famous for its beer. There are thousands of interesting brews to check out. FOSDEM even organises a beer event on Friday. Be careful though, Belgian beers are famously strong. Beer is also available during the day at the venue.

Make sure to stay hydrated (with water!) to avoid a massive hangover on the first day of the conference. Do not forget to follow the code of conduct, it also applies to all associated events.

Unfortunately, if you don’t drink alcohol, the Friday event may not be your vibe.


Have a great time!

Easily deploy your Hugo site to Github Pages

I had been using a DigitalOcean droplet to host this blog and personal projects for years. But… Do I really need to spend the $6/month? It’s just a static Hugo site, which Github pages can do for free. Game hosting on is also free, it even gives you a mechanism to embed your web games. Looks like I’d be sorted!

Here’s a simple CD workflow for deploying a Huge site to Github pages.

Setup your production Github pages repo

Create a Github repo with the name <username> Make sure it has a so that you can start pushing to it. Anything pushed to this repo will be accessible from http://<username> This will be your production repository. If you have a PRO Github account, you can make the repo private. This means users will be able to access the site but not the repo sources.

Create your dev repository

Create a new repo to host your dev Hugo site from which we will deploy to the production repo. Name it something like <username> That’s where you will write your Hugo pages in Markdown.

Configure the CD pipeline

Whenever we push to our dev repo, we will build the site and deploy it to the production repo. For that, we will leverage Github Actions. For the dev repo to publish to the production repo, we need them to exchange keys.

Generate a public/private key pair.

ssh-keygen -t rsa -b 4096 -C "<email>" -f publish_key -P ""

Now, go to the production repo Settings, Deploy keys and paste the contents of here.


Go to the dev repo Settings, Secrets and add a new secret called ACTIONS_DEPLOY_KEY with the contents of the publish_key file.


Create a .github/workflows/deploy.yml file in your dev repo with the following contents:

name: Build and deploy

    - master

    runs-on: ubuntu-latest
    - name: Checkout
      uses: actions/checkout@v1
    - name: Setup Hugo
      uses: peaceiris/actions-hugo@v2
    - name: Build
      run: hugo
    - name: Deploy
      uses: peaceiris/actions-gh-pages@v2
        EXTERNAL_REPOSITORY: <username>/<username>
        PUBLISH_BRANCH: master
        PUBLISH_DIR: ./public

After you push this file and every time you push to dev, the Build and Deploy action will be triggered. It will checkout the dev repo, install the latest Hugo binary, build your site and deploy to the master branch of your production repo. Once it’s done, the latest version of your site will be available on https://<username>


Even though my blog contains hundreds of posts, images and files, Build and Deploy takes less than 1 minute!

Make your domain point to your Github Pages

Obviously, this step only applies if you have your own domain. Go to your provider’s admin panel and change your domain’s DNS records.

  • Enter 4 A records with @ hosts and the Github Pages IPs:
  • Enter a CNAME record with www and value <username>

The screenshot below shows the Namecheap DNS config panel for my account.

Finally, go to your dev repo and create a static/CNAME file with your domain name. For example, mine contains:

Once that is done and deployed to production, you should see the following in your production repository Settings, inside the Github Pages section. Tick the “Enforce HTTPS” to redirect all traffic from http://<domain> to https://<domain>.


Note that this may take up to 24h to work as your new DNS settings propagate, so be patient!


It takes some setup but the result is a pretty convenient workflow, definitely an improvement over the git hook based setup I had before.

Let me know if you have any issues with this!