Installation
Prerequisites
- Node.js 18+ and npm (or your preferred package manager)
- TypeScript knowledge -- SAGE is written in TypeScript and expects you to use it
- A bundler like Vite (recommended), Webpack, or similar
Install Packages
SAGE requires BabylonJS and Havok as peer dependencies:
npm install @skewedaspect/sage @babylonjs/core @babylonjs/havokIf you are building a Vue application, also install the Vue integration package:
npm install @skewedaspect/sage-vueTypeScript Configuration
Make sure your tsconfig.json includes these settings:
{
"compilerOptions": {
"target": "ES2023",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"lib": ["ESNext", "DOM", "DOM.Iterable"]
}
}Quick Start: Spinning Cube
The fastest way to verify your setup is to get a cube on screen and spinning. This uses SAGE's createGameEngine() factory and BabylonJS directly for scene setup.
HTML
Create an index.html with a full-viewport canvas:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SAGE - Hello Cube</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
#gameCanvas {
width: 100%;
height: 100%;
touch-action: none;
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<script type="module" src="./src/main.ts"></script>
</body>
</html>Game Code
Create src/main.ts:
import { createGameEngine } from '@skewedaspect/sage';
import {
ArcRotateCamera,
Color3,
Color4,
HemisphericLight,
MeshBuilder,
StandardMaterial,
Vector3,
} from '@babylonjs/core';
async function main() : Promise<void>
{
const canvas = document.getElementById('gameCanvas') as HTMLCanvasElement;
// Create the engine -- no entity definitions needed for this demo
const engine = await createGameEngine(canvas, []);
// Create a scene with physics enabled
const scene = engine.engines.sceneEngine.createScene();
scene.clearColor = new Color4(0.12, 0.12, 0.14, 1);
// Camera
const camera = new ArcRotateCamera(
'camera',
Math.PI / 4,
Math.PI / 3,
5,
Vector3.Zero(),
scene
);
camera.attachControl(canvas, true);
// Light
new HemisphericLight('light', new Vector3(0, 1, 0), scene).intensity = 0.8;
// Cube
const cube = MeshBuilder.CreateBox('cube', { size: 1.5 }, scene);
const material = new StandardMaterial('cubeMat', scene);
material.diffuseColor = new Color3(0.5, 0.17, 0.17);
cube.material = material;
// Spin the cube every frame
engine.managers.gameManager.registerFrameCallback((dt : number) =>
{
cube.rotation.x += 0.3 * dt;
cube.rotation.y += 0.6 * dt;
scene.render();
});
// Start the game loop
engine.managers.gameManager.start();
}
main();Run it with Vite:
npx viteYou should see a dark scene with a red cube rotating. Click and drag to orbit the camera.
Vue Integration
If you are using Vue 3, the @skewedaspect/sage-vue package provides a SageCanvas component that handles engine creation, canvas management, and resize handling for you.
<template>
<SageCanvas @engine-ready="onEngineReady" :options="{ logLevel: 'info' }" />
</template>
<!--------------------------------------------------------------------------------------------------------------------->
<style scoped>
/* SageCanvas fills its parent, so size the parent */
</style>
<!--------------------------------------------------------------------------------------------------------------------->
<script setup lang="ts">
import type { GameEngine } from '@skewedaspect/sage';
import { SageCanvas } from '@skewedaspect/sage-vue';
function onEngineReady(engine : GameEngine) : void
{
// Set up your scene and start the game loop here.
// The engine is fully initialized -- create scenes,
// register frame callbacks, and call start().
}
</script>
<!--------------------------------------------------------------------------------------------------------------------->SageCanvas provides a scoped slot with loading, error, and engine properties, so you can overlay UI on top of the canvas:
<SageCanvas @engine-ready="onEngineReady">
<template #default="{ loading }">
<div v-if="loading">Loading...</div>
</template>
</SageCanvas>You can also pass entity definitions directly via the :entity-definitions prop instead of registering them manually after engine creation.
TIP
Child components can access the engine instance using the useSageEngine() composable from @skewedaspect/sage-vue, without needing to pass it as a prop.
Next Steps
This page got you from zero to spinning cube. For the full walkthrough with explanations of every piece, see the Hello Cube Guide. From there:
- Architecture -- understand how the engine is put together
- Entities -- learn the entity-behavior composition system
- Input & Bindings -- set up keyboard, mouse, and gamepad input
- Levels -- load scenes from YAML and GLB files
