Ventilastation — reading view

Download the Markdown source

A 12-minute introduction for everyone, followed by 15 minutes of coding and three Dream Garden parts. Each slide includes pacing and speaker notes.

Ventilastation

Games on a fan

Alejandro J. Cura - alecu@protocultura.net

COLLIDERS Meet-up – Circular Arcades

14 October 2026

Speaker notes

0:00–0:20. Introduce the console and the people around it before showing any code. The first twelve minutes are for everyone, including people who do not program. The following fifteen minutes focus on making a circular game. We will launch only three cumulative Garden versions. Questions and open play follow the timed talk.

Source: Sickhouse’s Circular Arcades meetup. Cover photo: this website’s images/banner.jpg.


Cybercirujas

An Argentine collective that repairs and recirculates technology.

Shared knowledge, free software and resistance to planned obsolescence.

Ventilastation is part of that community.

The Cybercirujas community gathered among reused computers and electronics

Speaker notes

0:20–1:30. Cirujeo means finding useful things among what others discard. Cybercirujas extend that practice to computers and electronics, and share the knowledge needed to keep them useful. People contribute repairs, parts, software, teaching and curiosity. Connect this to Ventilastation: an unexpected object becomes a place for shared play. Explain the relationship personally rather than presenting it as a commercial sponsor. Do not imply that this photo shows a specific event or identify people in it.

Source: UNQ’s account of Cybercirujas, including its interviews with the collective. Photo supplied by Alejandro Cura as cybercirujas.jpg.


What is Ventilastation?

A player holding a joystick in front of Ventilastation’s glowing fan display

Speaker notes

1:30–2:10. A playable arcade console inside a fan. Point to the controller, the circular image and the player: this is an unusual display people can make games for. The next slide looks inside the console.

Photo supplied by Alejandro J. Cura as vs_gameon.jpg.


A game console built around a fan

A rotating LED arm draws a circular screen.

An ESP32-S3 runs the game in MicroPython.

Built in Argentina. Open source and open hardware.

Ventilastation’s LED arm and electronics mounted inside a fan

Speaker notes

2:10–3:10. Point to the moving arm, electronics and fan rather than listing components. It is a console people can write games for, not just a video loop. The code runs on the rotating board. A separate base handles sound and controllers. People can develop on a laptop without building their own hardware.

Sources: Ventilastation, current SDK introduction. Photo: this website’s images/pic01.jpg.


Light becomes a screen through motion

The arm paints a different slice at each angle.

Our eyes join the slices into a disc.

The image has a centre and a rim, with no corners.

Speaker notes

3:10–4:10. Play the physical display clip and trace the arm’s rotation with your hand. Each LED follows a circle. Changing its light as it rotates makes a coloured arc. Persistence of vision joins the successive angles into the image. Unlike a stationary rectangular panel, positions on the screen arrive at different instants. The game update and motor rotation are separate clocks. Keep this intuitive for the non-coding audience.

Sources: Physical display, display geometry. Video: this website’s images/banner.mp4.


Arxels get smaller toward the centre

An arxel is a tiny arc of light.

The same angle covers a wider arc at the rim.

Near the centre, arxels get narrower and details crowd together.

The same eight-by-eight block of LED cells appears broad at the rim and progressively narrower nearer the centre

Speaker notes

4:10–5:20. Arxel combines arc and pixel. These three blocks use the same eight angular columns and eight radial LEDs. Their angular width stays constant, but their physical width shrinks at the smaller radii. Radial LED spacing stays fixed. Distinguish the size of a light cell from an actor shrinking through a tunnel projection. The image uses the HUD projection to isolate the radial geometry. Explain why small lettering and fine artwork become hard to read near the centre. No maths is needed here.

Source: Current display geometry. Image: actual font block cells rendered through the WebGL display backend.


Ordinary artwork bends around the disc

The pink dreamer’s ordinary rectangular pixel-art poses

PNG artwork becomes curved on the display.

It stretches along the rim, compresses inward and turns with its angle.

The same dreamer sprite rotates with its position at the bottom, left, top and right of the disc

Speaker notes

5:20–6:10. Show the original flat art and then the four copies around the rim. The renderer maps its horizontal pixels to angular columns and its vertical pixels to radial positions. The result bends and changes proportions. Its local upright direction rotates around the disc. The dreamer’s antennae point inward at every angle. Critical details need to survive that transformation, and a conventional game screen pasted into a circle will not behave like its rectangular original. Invite artists to treat this as a design material.

Source: Sprite geometry. Images: the real dreamer strip and a WebGL rendering of those assets.


Two joysticks, music and sound

Two joysticks provide directions and action buttons.

Games can compete, cooperate or share a space.

Speakers carry music, effects and feedback beyond the disc.

Speaker notes

6:10–7:10. Hold up or point to the two physical controllers. Explain that the player input has the familiar joystick directions and buttons, with room for one- and two-player games. The screen is unusual, but those inputs do not require inventing new controllers. Music and effects play through the base station, not through a speaker on the spinning arm. Audio can carry mood, rhythm and events that the crowded centre cannot show clearly. Keyboard controls and the desktop emulator substitute for the hardware during development.

Sources: Controllers and audio, base station.


My sample games

Speaker notes

7:10–8:10. Show two of my sample games. Super Ventilagon turns rotation and approaching obstacles into an arcade survival game. Vyruss puts a space shooter on the circular display. The screenshots show play rather than their starting screens. Keep the history brief and point out how each uses the disc.

Sources: Super Ventilagon, Vyruss. Images: actual gameplay captured with the WebGL display backend.


Past jam creations

Speaker notes

8:10–9:20. These games came from earlier community jams. Senile command defends the fan’s core against incoming fingers. Vasura Espacial is a space shooter defending a planet. Tincho el Carpincho turns a running and jumping course into a circle. Credit the makers collectively and link to their original source folders. These are playable creations, not a prescribed genre for the new jam.

Sources: Senile command, Vasura Espacial, Tincho el Carpincho. Images: actual gameplay captured with the WebGL display backend.


Scenes are moments in an experience

Speaker notes

9:20–10:10. A scene is one active moment: a title, the playable garden, a pause, a result or a rest. It can have its own pictures, controls and sound decisions. This need not mean lives and a game-over screen. Garden voluntarily switches to Rest, preserving what players planted. Explain the concept before its Python interface so artists and designers can plan an experience too.

Source: Dream Garden’s Title, Garden and Rest. Images: those actual scenes through the WebGL display backend.


The pieces of a game image

Layers choose what draws on top.

Sprites are movable pictures.

Tilemaps assemble repeated picture cells.

Labels turn font cells into text.

Full-screen images cover the disc with one picture.

Flowers and dreamers below clouds and seeds, with RELAX text drawn on top

Speaker notes

10:10–11:10. Point at the image while naming the pieces. Flowers and dreamers form the lower layer. Clouds and golden seeds occupy the middle layer. RELAX is a label on the top layer. The creatures are sprites, and a large cloud spans several tilemap cells. A full-screen image is another option for a backdrop or illustration covering the disc; this garden instead uses sparse clouds. These are tools for visual composition. Avoid explaining constructors, reserve sizes or projection constants in the non-coder section.

Sources: Drawables, projections. Image: the completed garden while holding A, captured through WebGL.


Ventilastation × The Overkill Jam

Starts 14 October 2026

Games will meet players at The Overkill, 26–29 November.

Artists, musicians, writers, programmers and curious playtesters are welcome.

itch.io/jam/ventilastation-overkill-jam

Speaker notes

11:10–12:00. Close the introduction with the invitation, so people who leave before the code still know how to join. The jam page lists October 14 through November 11 for submissions. Link to it for the authoritative times and current instructions rather than adding a timezone guess. The resulting games are intended for the real console at The Overkill in late November. Find collaborators with different skills. The next fifteen minutes show enough code to begin experimenting.

Sources: Official jam page, Sickhouse’s November 26–29 festival announcement.


How to code for Ventilastation

Speaker notes

12:00–12:20. Open the coding part. The next fifteen minutes cover the display-specific ideas needed to make a first game, with three playable versions of Dream Garden.


A game is a folder on your laptop

games/myname/mygame/
  code/mygame.py
  images/  # PNG strips and __images__.yaml
  sounds/  # optional MP3s
  menu.png
  meta.json
./vs-emu.sh --game myname.mygame

Edit local Python files and test with the desktop emulator.

Speaker notes

12:20–13:15. Continue from the coding title slide. Use an up-to-date SDK main checkout, where Dream Garden is already included. Dependencies should be installed before the session; link to the desktop guide rather than spending the fifteen minutes on setup. Windows uses vs-emu.bat. Explain the folder name, matching Python module and images manifest. main() returns the starting scene. Metadata declares api: vs2 and api_revision: 2. The menu icon is 64×30. Artists can edit the PNGs without changing game logic, and audio lives as MP3s on the host or console base. The complete Garden download includes the assets and runnable code.

Sources: Desktop setup, complete Garden download.


Part 1: one creature around the rim

Play part 1

import vs2
from vs2.controls import LEFT, RIGHT, joy1

class Game(vs2.Scene):
    def build(self):
        world = self.layer("world", projection=vs2.TUNNEL)
        self.dreamer = world.sprite("dreamer.png", y=0, x=-9)
    def update(self):
        steer = joy1.held(LEFT) - joy1.held(RIGHT)
        self.dreamer.x = (self.dreamer.x + steer) % 256

def main():
    return Game()

A / D steer. Crossing column zero stays continuous.

Speaker notes

13:15–15:45. This is the whole starter’s behaviour, with x=-9 centring the 19-pixel dreamer at angle zero. The downloadable starter derives that offset from its width. The runnable part uses the same result. Explain the unusual part: X is an angular position and wraps, while Y zero sits at the outer rim. Held directions are stable controller states; opposite directions cancel. Open part one in its own tab, move across the seam and return to the deck. Reserve about one minute for this demonstration. The packed dreamer image and current metadata complete the folder. Everyone can replace its artwork; programmers can change the movement.

Sources: Starter source, display model.


Angle and depth replace screen X and Y

X wraps through 256 angles.

TUNNEL Y is depth from 0 to 255. Increasing it moves inward and shrinks the picture.

HUD Y selects one of 54 radial LED positions.

Angular coordinates start at the bottom and wrap around the disc, while radial positions point inward

Speaker notes

15:45–17:15. Connect this to the arxels from the introduction. X zero is the bottom, 64 the left, 128 the top and 192 the right. Positive motion therefore turns left at the bottom. Use display.width in general code; the previous slide’s 256 states this console’s geometry plainly. TUNNEL adds a depth transformation on top of the radial cells, so moving inward also shrinks an actor. Its range is not the physical LED count. HUD uses LED rows directly and suits labels near the rim. Do not treat display.height as the point where a tunnel seed has left the world. Test both the angular seam and the centre with actual assets.

Source and diagram: Circular coordinates and projections.


Reserve pictures, then move them

def build(self):
    self.seeds = self.air.sprite_pool("seed.png", count=16)

def update(self):
    for seed in self.seeds:
        seed.y -= self.speed * 0.85

build() creates the display graph at scene entry.

update() changes its existing objects every 30 ms.

Speaker notes

17:15–18:45. This is the runtime rule worth retaining from the longer presentation. Build the layers and reserve sprites, pools, tilemaps and labels at scene entry. Returning from build seals that graph. A catch reuses a reserved flower slot rather than creating a fresh renderer object. Spawn reveals a slot and despawn retires it. The finished Garden reserves sixteen seed slots and twenty-four flowers, recycling the oldest flower when full. We do not need a budget table or Python allocation lecture on part; those details remain in the reference. The excerpt uses the finished game’s air layer. Ordinary game state and drawable handles are separate when a scene is rebuilt.

Sources: Scene lifecycle, pools, finished Garden.


Part 2: catching seeds grows flowers

Play part 2

for seed in self.seeds:
    seed.y -= self.speed * 0.85
    if seed.overlaps(self.dreamer):
        self.bloom(seed)

The overlap test understands the angular seam.

Flowers start at the player’s depth and drift inward.

Part two’s pink dreamer catches golden seeds and grows flowers, before clouds or HUD text are introduced

Speaker notes

18:45–21:15. Open the second cumulative version. It adds timed seeds, collision and planting, but keeps one player and leaves clouds, HUD, scenes and sound for the final version. The overlap test uses angular and depth bounds, including wrap at column zero. Explain only the response needed to see the game’s shape: plant at the catching player’s Y, offset 75% of their width toward movement. After stopping, the first catch follows the last direction and later catches alternate. Flowers drift inward far more slowly than seeds move outward. Misses do no harm. Have the audience predict which side a flower will appear on, then demonstrate. The full helper is in the download; avoid reading its arithmetic line by line. Reserve roughly a minute for live play.

Sources: Game instructions and source, collision. Image: the actual planting checkpoint through WebGL.


Art stays separate from game logic

The original atlas of dreamers, seed frames and flowers The separate atlas of six fluffy clouds in three sizes

PNG strips hold frames. Palette groups hold up to 256 colours.

Speaker notes

21:15–22:55. Keep both atlases from the original presentation, but reduce the asset pipeline to what matters here. The runtime builds an indexed image ROM from the PNG strips and YAML manifest. Dreamers and flowers share one palette group; clouds have another so they can stay subdued. Animated frames have equal dimensions and sit side by side in a strip. The large clouds become adjacent 16-pixel cells, so one shape can span two cells or a three-by-two block. Original art stays separate from code and can be replaced. Point artists at the downloadable files. Test the result on the disc, because rectangular art changes proportions and details compress near the centre. Leave conversion scripts and frame-count minutiae to the guide.

Source: Artwork and rebuild recipes. Images: the original source atlases, unchanged.


Depth, draw order and readable text

self.air = self.layer("air", projection=vs2.TUNNEL)
self.ground = cloud_tiles(self.air)
self.seeds = self.air.sprite_pool("seed.png", count=16)

hud = self.layer("hud", projection=vs2.HUD)
hud.label("steel8x8.png", columns=5, x=108, y=1,
          flip_x=True, flip_y=True, text="RELAX")

Clouds first, seeds above them. HUD text near the rim.

Speaker notes

22:55–24:25. This is an excerpt from the complete version, following its lower world layer. Layers draw in creation order, and drawables draw in creation order inside each layer. Reserve clouds before seeds, and flowers before players. A scrolling tilemap changes its view instead of creating new actors. The map and sprites share the tunnel projection, so both curve into the disc. HUD uses LED rows, keeping text near the wider outer cells. At the top, flip both axes so text reads upright. The full-screen projection is also available for a single centred picture, while the completed Garden uses lower world, middle air and top HUD. These are display-specific choices; the collision and input logic will feel familiar to experienced developers.

Sources: Draw order and projections, tilemaps and text, finished source.


Part 3: the completed shared garden

Play the finished game

Invite a second player into the same garden.

Hold A to relax the seed flow. Clouds keep moving.

N toggles music and sounds. B keeps the flowers in a resting scene.

The completed Dream Garden with two players, clouds, golden seeds and flowers

Speaker notes

24:25–26:35. Run the completed game, exactly the final source. A begins. A/D steer player one, arrows steer player two, and Space or numpad zero asks for slower seeds. Release the start press before holding A to relax. Show the three visible layers and the way golden seeds remain above the clouds. Point out an occasional glow and the flowers’ much slower inward drift. Either player can slow seeds; clouds and flower animation keep their own steady pace. Planting direction is independent for each player. Music and bloom sounds start on. N switches audio, with a two-second NOISY or QUIET notice that slides out. B voluntarily rests with the planted flowers; A begins another dream. Keep the demonstration focused on the differences of this display and the shared interaction. The default desktop setup is the development path; the browser launch is a quick demonstration. No hardware assembly is required to join the jam.

Sources: Complete source, game guide and download. Image: the real populated game through WebGL.


Your next circular game

Join the jam on itch.io

Desktop setup

Dream Garden source and complete download

Ventilastation API

Speaker notes

26:35–27:00. Finish the timed coding section at minute twenty-seven. Ask people to form teams and choose one interaction that uses the centre, the rim, rotation or shared play. The linked guide and reference cover the material omitted from this shorter talk. Stay for questions and open play without assigning them part of the fifteen-minute coding slot.

Sources: the linked official jam page and current documentation.