This guide walks you through installing Fraxel, configuring your TypeScript project to use the custom JSX runtime, and building a working game scene complete with textures, reactive state, and player input. You will have a canvas rendering on screen by the end of Step 3.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 has no runtime dependencies. The package includes the engine, the custom JSX runtime, all built-in nodes, hooks, and the asset pipeline.
Fraxel provides its own JSX transform through a custom
jsxImportSource. You must configure TypeScript to use it instead of React:{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"verbatimModuleSyntax": true,
"jsx": "react-jsx",
"jsxImportSource": "fraxel"
}
}
verbatimModuleSyntax: true requires that type-only imports use the explicit type keyword. For example, write import type { GameControls } from 'fraxel/jsx' for any import that is used only as a TypeScript type. Regular value imports (import { useSignal } from 'fraxel/hooks') are not affected.jsx: "react-jsx" — tells TypeScript to use the automatic JSX transform (i.e. jsx/jsx-runtime)jsxImportSource: "fraxel" — points the automatic transform to fraxel/jsx-runtime instead of react/jsx-runtimeWith these two options set, every
.tsx file in your project will use the Fraxel engine as its JSX runtime automatically. No import needed.Create a
main.tsx file at your project root. This example loads two textures, defines a scene as a JSX function, wires it into a <Game> configuration, and starts the game loop:import { createGame, Game, Scene } from 'fraxel/jsx'
import { loadTexture } from 'fraxel/assets'
// Load textures before creating the game.
// loadTexture returns a symbol ID used to reference the texture in JSX.
const BG = await loadTexture('/assets/background.png')
const PLAYER = await loadTexture('/assets/player.png')
// A scene component is a plain function that returns JSX.
const scene = () => (
<transform>
<sprite textureId={BG} />
<sprite textureId={PLAYER} position={[80, 50]} />
</transform>
)
// createGame processes the JSX tree, creates the canvas, and mounts it
// inside the element matched by the selector.
const game = createGame(
<Game width={192} height={112} defaultScene="main">
<Scene name="main" component={scene} />
</Game>,
document.querySelector('#root')!,
)
game.play()
createGame calls game.play() internally to start the requestAnimationFrame loop immediately after setup. The explicit game.play() call at the end is only needed if you paused the game or want to defer startup — it is safe to call it again.loadTexture(url) — fetches an image and returns a symbol identifier. Pass this symbol to any textureId prop.createGame(jsx, root) — processes the <Game> JSX tree, calls Game.setup() with the declared dimensions, registers each <Scene>, and sets the default scene.<Game> — declarative game configuration. width and height define the canvas resolution. defaultScene names the scene that loads immediately.<Scene> — registers a named scene. The component prop accepts a plain function or a lazy () => import(...) for code splitting.game.play() — starts the requestAnimationFrame loop.Use hooks inside your scene components to add reactive state, events, and per-frame logic. Here is a
Player component that moves across the screen and turns grayscale when health reaches zero:import { useNode, useEvent, useSignal } from 'fraxel/hooks'
import { PrimaryNode } from 'fraxel'
function Player() {
// useNode creates a typed reference. Pass it to a node's `ref` prop.
const sprite = useNode(PrimaryNode.Sprite)
// useSignal returns a [getter, setter] tuple.
// Call health() to read the value — calling it inside a prop function
// registers it as a dependency automatically.
const [health, setHealth] = useSignal(100)
// useEvent subscribes to a node lifecycle event.
// 'updated' fires every frame with the delta time in seconds.
useEvent(sprite, 'updated', (delta) => {
sprite.node.position.x += delta * 50
})
return (
<sprite
ref={sprite}
textureId={PLAYER}
grayscale={() => (health() <= 0 ? 1 : 0)}
/>
)
}
useEvent(sprite, 'updated', ...) is the idiomatic way to run per-frame logic in Fraxel. The delta argument is the time elapsed since the last frame in seconds, giving you frame-rate-independent movement without managing your own timers.Import Paths
Fraxel is split across six sub-path exports so you only import what you use. Each path is independently tree-shakeable:With
verbatimModuleSyntax: true in your tsconfig, any import used only as a type must use import type. For example: import type { GameControls } from 'fraxel/jsx'.Next Steps
Core Concepts
Understand the node tree, signal reactivity, and how the JSX runtime works under the hood.
Nodes Reference
Explore every built-in JSX element:
<transform>, <sprite>, <collider>, <camera>, <text>, and more.Hooks API
Full reference for
useSignal, useComputed, useEffect, useMount, useNode, useEvent, and useSpawn.