Documentation Index
Fetch the complete documentation index at: https://mintlify.com/sanchedev/fraxel/llms.txt
Use this file to discover all available pages before exploring further.
Fraxel provides a unified input system that handles both pointer (mouse / touch) and keyboard events. The Input instance is created automatically by Game.setup() and exposed as Game.input. Pointer coordinates are automatically scaled from CSS pixels to game canvas pixels so they always match the coordinate space your nodes live in. Keyboard methods support modifier keys and per-frame “just pressed / just released” queries for clean game-feel logic.
Properties
| Property | Type | Description |
|---|
input.pointerPosition | Readonly<Vector2> | Current pointer position in game canvas coordinates (read-only) |
input.isPointerPressed | boolean | true while any pointer button is held down |
Pointer Events
Subscribe to pointer events via the named event properties on the Input object. All pointer events pass the current game-coordinate Vector2 to their callbacks.
Property on input | Callback signature | Description |
|---|
pointerPressed | (position: Vector2) => void | Fires when the pointer is pressed |
pointerUnpressed | (position: Vector2) => void | Fires when the pointer is released |
pointerMoved | (position: Vector2) => void | Fires whenever the pointer moves |
import { Game } from 'fraxel'
Game.input.pointerPressed.on((position) => {
console.log('Pressed at', position.x, position.y)
})
Game.input.pointerUnpressed.on((position) => {
console.log('Released at', position.x, position.y)
})
Game.input.pointerMoved.on((position) => {
console.log('Moved to', position.x, position.y)
})
All keyboard methods accept an optional set of modifier key flags. Keys are matched case-insensitively.
Methods
| Method | Return | Description |
|---|
isKeyPressed(key, ctrl?, shift?, alt?) | boolean | true while the key is currently held |
isJustKeyPressed(key, ctrl?, shift?, alt?) | boolean | true only on the first frame the key was pressed |
isJustKeyUnpressed(key, ctrl?, shift?, alt?) | boolean | true only on the first frame the key was released |
getKeyAxis(positiveKey, negativeKey) | -1 | 0 | 1 | Returns 1, -1, or 0 based on held keys |
getKeyAxis(positiveKey, negativeKey) returns 1 if the positive key is held, -1 if the negative key is held, 0 if neither or both are held. This is ideal for directional movement: getKeyAxis('d', 'a') gives you a horizontal axis for left/right control.
import { useNode, useEvent } from 'fraxel/hooks'
import { PrimaryNode, Game, Vector2 } from 'fraxel'
function Player() {
const transform = useNode(PrimaryNode.Transform)
useEvent(transform, 'updated', (delta) => {
const input = Game.input
// Continuous movement using axis
const dx = input.getKeyAxis('d', 'a')
const dy = input.getKeyAxis('s', 'w')
transform.node.position.x += dx * 80 * delta
transform.node.position.y += dy * 80 * delta
// One-shot action on frame of press
if (input.isJustKeyPressed(' ')) {
console.log('Jump!')
}
})
return <transform ref={transform}><sprite textureId={PLAYER} /></transform>
}
The Clickable Node
For per-node pointer hit-testing, use the <clickable> node. It performs an AABB check against the pointer position every frame and emits typed events when the pointer enters, exits, or clicks within its rectangular area.
import { useNode, useEvent } from 'fraxel/hooks'
import { PrimaryNode } from 'fraxel'
function Button() {
const btn = useNode(PrimaryNode.Clickable)
useEvent(btn, 'clicked', (pos) => {
console.log('Button clicked at local position', pos.x, pos.y)
})
useEvent(btn, 'mouseEntered', () => {
console.log('Pointer entered button area')
})
useEvent(btn, 'mouseExited', () => {
console.log('Pointer left button area')
})
return (
<clickable ref={btn} size={[64, 32]}>
<sprite textureId={BUTTON_TEX} />
</clickable>
)
}
Props
| Prop | Type | Default | Description |
|---|
size | VectorLike | — | Width and height of the clickable area. Required. Accepts [w, h], {x, y}, or a single number for a square |
disabled | boolean | false | When true, all hit-testing and event emission is suspended |
Events
| Event | Callback signature | Description |
|---|
clicked | (position: Vector2) => void | Fires when pointer is released inside the area (local coordinates) |
mouseEntered | () => void | Fires once when the pointer enters the area |
mouseExited | () => void | Fires once when the pointer leaves the area |
mouseOver | (position: Vector2) => void | Fires every frame while the pointer is inside the area |
JSX shorthand callbacks
The <clickable> element also accepts inline callback props for convenience:
<clickable
size={[64, 32]}
onClick={(pos) => console.log('click', pos)}
onMouseEnter={() => console.log('enter')}
onMouseExit={() => console.log('exit')}
/>
Debugging clickable areas
Enable testOptions.showClickables when setting up the game to draw a visible overlay over every clickable area:
Game.setup({
width: 160,
height: 90,
root,
testOptions: { showClickables: true },
})
Active clickable areas are drawn in a golden tint; disabled ones are drawn in grey.
Pass inputOptions to Game.setup() to configure how the input system handles keyboard events:
Game.setup({
width: 160,
height: 90,
root,
inputOptions: {
preventKeyDefaults: true, // default — blocks browser shortcuts (arrow keys, space, etc.)
},
})
| Option | Type | Default | Description |
|---|
preventKeyDefaults | boolean | true | Calls preventDefault() on keyboard events to block browser shortcuts |
Set preventKeyDefaults: false if your game runs alongside a page that needs normal keyboard behaviour (e.g. text input fields outside the canvas).
Cleanup
The Input instance registers several window event listeners (keydown, keyup, pointermove, pointerdown, pointerup, resize). Call input.destroy() when you are done with the game to remove all of them and prevent memory leaks:
import { Game } from 'fraxel'
// Tears down the game loop and removes all input listeners
Game.destroy()
Game.destroy() calls Game.input.destroy() internally, so you do not need to call it separately when using the Game class. If you ever instantiate Input manually, remember to call destroy() yourself.
For a full example of player movement combined with physics, see Physics. For per-node event hooks, see API: Hooks (Core).