Keel uses a typed, per-frame event bus to communicate between systems without tight coupling. AnyDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/VKSFY/keel/llms.txt
Use this file to discover all available pages before exploring further.
@keel.event-decorated class becomes an event type. Instances are queued with world.emit() and iterated with world.read_events(). All queues are cleared automatically at the start of every visual frame, so events emitted in tick N are readable by later systems in tick N but gone by tick N+1.
Defining Event Types
@keel.event
Marks a class as an event type. If the class is not already a dataclasses.dataclass, @keel.event applies @dataclass automatically. A __keel_event__ = True attribute is set on the class to tag it for internal bookkeeping.
@dataclass yourself first — @keel.event is idempotent with respect to dataclass decoration:
Emitting and Reading Events
world.emit(event_instance)
Queue an event instance for the current frame. The event is appended to the per-type queue and is immediately readable by any system that runs later in the same frame (including later ticks of the same simulation phase).
An instance of a
@keel.event-decorated class.world.read_events(event_type) -> Iterator
Iterate over all events of event_type queued this frame. Returns an empty iterator if no events of that type were emitted.
The
@keel.event class to filter on.An iterator over all queued instances of
event_type for this frame. Iteration does not consume events — multiple systems can read the same queue independently.Built-in Input Events
These event types are emitted automatically by Keel’s GLFW callback system whenever the corresponding hardware event occurs. They are all@keel.event dataclasses defined in keel.input.
KeyEvent
Emitted on key press and repeat transitions. Not emitted on key release (use InputState.is_key_released() for that).
GLFW key code — one of the
keel.KEY_* constants (e.g. keel.KEY_SPACE, keel.KEY_W).Platform-specific raw scancode for the physical key.
keel.PRESS or keel.REPEAT.Bitfield of active modifier keys (GLFW modifier constants).
MouseButtonEvent
Emitted on every mouse button press and release.
GLFW mouse button constant —
keel.MOUSE_BUTTON_LEFT, keel.MOUSE_BUTTON_RIGHT, keel.MOUSE_BUTTON_MIDDLE, or MOUSE_BUTTON_4 through MOUSE_BUTTON_8.keel.PRESS or keel.RELEASE.Active modifier key bitfield.
MouseMoveEvent
Emitted whenever the cursor moves within the window’s content area.
Cursor X position in window pixel coordinates (origin at top-left).
Cursor Y position in window pixel coordinates (origin at top-left).
MouseScrollEvent
Emitted on scroll wheel movement.
Horizontal scroll delta (positive = right).
Vertical scroll delta (positive = up / away from user).
WindowResizeEvent
Emitted when the framebuffer is resized (e.g. the user drags the window edge). Width and height are in pixels, not logical screen coordinates.
New framebuffer width in pixels.
New framebuffer height in pixels.
GamepadButtonEvent
Emitted on every gamepad button press or release state flip.
GLFW joystick/gamepad ID (0-based index).
GLFW gamepad button constant — one of
keel.GAMEPAD_BUTTON_A, GAMEPAD_BUTTON_B, GAMEPAD_BUTTON_X, GAMEPAD_BUTTON_Y, GAMEPAD_BUTTON_LEFT_BUMPER, GAMEPAD_BUTTON_RIGHT_BUMPER, GAMEPAD_BUTTON_BACK, GAMEPAD_BUTTON_START, GAMEPAD_BUTTON_GUIDE, GAMEPAD_BUTTON_LEFT_THUMB, GAMEPAD_BUTTON_RIGHT_THUMB, GAMEPAD_BUTTON_DPAD_UP, GAMEPAD_BUTTON_DPAD_DOWN, GAMEPAD_BUTTON_DPAD_LEFT, GAMEPAD_BUTTON_DPAD_RIGHT.keel.PRESS or keel.RELEASE.GamepadAxisEvent
Emitted when a gamepad axis moves past the 0.05 emit-deadzone since the last poll. Only fired when movement is significant enough to avoid noise flooding.
GLFW joystick/gamepad ID.
GLFW axis constant — one of
keel.GAMEPAD_AXIS_LEFT_X, GAMEPAD_AXIS_LEFT_Y, GAMEPAD_AXIS_RIGHT_X, GAMEPAD_AXIS_RIGHT_Y, GAMEPAD_AXIS_LEFT_TRIGGER, GAMEPAD_AXIS_RIGHT_TRIGGER.Axis value in the range
[-1.0, 1.0] (triggers: [-1.0, 1.0] at rest to fully pressed).Built-in Physics Events
Physics events are emitted by thePhysics2D and Physics3D bridges during their POST_UPDATE step.
CollisionEvent2D
Emitted by Physics2D when two collidable shapes make contact during a physics tick.
First entity in the collision pair.
Second entity in the collision pair.
X component of the collision normal vector (points from B toward A).
Y component of the collision normal vector.
Magnitude of the collision impulse applied to resolve the contact.
KINEMATIC vs KINEMATIC bodies do not emit CollisionEvent2D due to pymunk callback semantics. Use DYNAMIC body type for entities that must detect collisions with each other.CollisionEvent3D
Emitted by Physics3D when two bodies report a contact during a physics tick.
First entity in the contact pair.
Second entity in the contact pair.
X coordinate of the contact point in world space.
Y coordinate of the contact point in world space.
Z coordinate of the contact point in world space.
X component of the contact normal.
Y component of the contact normal.
Z component of the contact normal.
Full Custom Event Example
This example walks through defining a custom event type, emitting it from one system, and reading it in a downstream system within the same phase. Step 1 — Define the event type:Because
apply_pickup_rewards declares after=pickup_items, the scheduler guarantees pickup_items has finished emitting ItemPickedUp events before apply_pickup_rewards starts reading them — even though both systems are in POST_UPDATE.Event Lifetime
All event queues are cleared byworld.events.clear(), which the run loop calls at the top of every visual frame before polling GLFW. This means:
- Events emitted during frame N are not present in frame N+1.
- Multiple simulation ticks within the same visual frame all share the same event queues — events emitted during tick 1 are still readable during tick 2.
world.tick(dt)(the headless manual driver) clears events at the start of each call, not the end.
