kinetik-engine
v0.1.35
Published
Kinetik engine and project initializer for Three.js games
Readme
Kinetik Engine
A lightweight 3D game engine built on Three.js for desktop (Electron) and mobile (Capacitor/Android). Provides a scene graph, physics, input, level loading, a built-in visual editor, save management, and spatial audio out of the box.
Core Modules
| Module | Purpose |
|---|---|
| globals.js | Shared gameState object and engine constants |
| scene.js | Renderer, camera, lighting, resize, TV knob interaction |
| physics.js | Player collision, movement, gravity, interact/tooltip hooks |
| input.js | Keyboard/mouse input, freecam, pause toggle hook |
| mobileControls.js | Touch joystick and mobile freecam |
| levelLoader.js | Loads .json level files, spawns meshes, CSG, image models |
| stateManager.js | Stateful object system, enter/exit sounds, level vars |
| saveManager.js | Cross-platform save slots (Capacitor Preferences / localStorage) |
| editor.js | Full visual level editor (launch with --editor flag) |
| settings.js | Persistent player settings |
Quick Start — New Game Project
Create a folder and add Kinetik as a submodule
mkdir my-game && cd my-game && git init git submodule add https://github.com/Jacqueb-1337/kinetik-engine.git src/coreRun the scaffold script — copies
electron-main.js,preload.js,package.json,vite.config.js,.gitignore, starterscripts/files, and all required asset directories into the project root:node src/core/init.jsSet your game name — open the generated
package.jsonand updatename,build.appId, andbuild.productName.Install dependencies:
npm installCreate your entry point (
src/main.js):import { gameState } from './core/globals.js'; import { initScene, updateCamera } from './core/scene.js'; import { initInput } from './core/input.js'; import { loadLevel } from './core/levelLoader.js'; import { update as physicsUpdate } from './core/physics.js'; import { updateStateAnimations } from './core/stateManager.js'; await initScene(); initInput(); await loadLevel('main'); function loop(delta) { physicsUpdate(delta); updateCamera(); updateStateAnimations(delta); gameState.renderer.render(gameState.scene, gameState.camera); }Create
levels/main.json— start with an empty level:{ "objects": [], "playerStart": { "x": 0, "y": 1, "z": 0 } }Run:
npm start # Electron desktop (game) npm run editor # Electron desktop (visual editor) npm run dev # Browser dev server (open /src/index.html)
Scene & Input Options
Both initScene() and initInput() accept an options object. All options are
optional and defaults preserve the historical behavior.
initScene({
background: 0x17142a, // scene background color
fogDensity: 0.016, // FogExp2 density — pass 0 to disable fog
fogColor: 0x17142a, // defaults to background
antialias: true, // renderer antialiasing (default false)
autoFullscreen: false, // don't request fullscreen on click (default true)
autoPointerLock: true, // request pointer lock on click (default true)
lockKeyboard: true, // capture Escape via Keyboard Lock API (default true)
});
initInput({
// Disable or remap the built-in hotkeys (pause/debug/camera/export/freecam).
// Pass hotkeys: false to disable all of them, or override individually:
hotkeys: { freecam: false, export: false, cameraMode: 'F6' },
});Edge-triggered input
initInput() also tracks per-frame pressed/released state so games don't have
to build their own edge detection:
import { initInput, endInputFrame } from 'kinetik-engine/input.js';
initInput();
function loop(delta) {
if (gameState.keysPressed['Space']) jump(); // true only on the press frame
if (gameState.mousePressed[0]) attack(); // left mouse button edge
if (gameState.keysReleased['KeyE']) stopChannel();
// ... game update + render ...
endInputFrame(); // clear edge state — call once at the END of each frame
}gameState.keys (held keys) and gameState.mouseButtons (held buttons) remain
available for continuous input.
Visual Editor
Launch with the --editor flag:
npm run editor # equivalent to: electron . --editorThe editor opens initEditor() from editor.js instead of the game loop. From there you can:
- Place, move, rotate, and scale meshes
- Paint CSG cuts and boolean geometry
- Add stateful objects with enter/exit sounds and animations
- Attach per-scene scripts and per-object scripts from the inspector
- Set level variables and trigger conditions
- Place player spawn, save triggers, and interact zones
- Export the scene back to
levels/<name>.json
To open the editor in a new project, follow the Quick Start above and use npm run editor.
The editor also runs in a plain browser (e.g. npm run dev, then open
/src/editor.html). Outside Electron it loads levels over HTTP from levels/
and Save downloads the level JSON so you can drop it into your project's
levels/ folder. Model/actor import still requires Electron.
Spatial Audio
import { playSound } from './core/stateManager.js';
// Play a sound at a world position — handles distance falloff,
// stereo panning, masterVolume, and ogg/mp3/wav format fallback.
playSound('explosion', new THREE.Vector3(4, 0, -10));Drop audio files in sounds/ as <name>.ogg, <name>.mp3, or <name>.wav — the engine tries each in that order.
Save System
import { triggerSave, loadSave, registerSaveExtension } from './core/saveManager.js';
// Register custom data to save/restore
registerSaveExtension('myGame', {
capture: () => ({ score, inventory }),
restore: (data) => { score = data.score; inventory = data.inventory; }
});
await triggerSave('slot1');
await loadSave('slot1');Works on desktop (Electron file system) and mobile (Capacitor Preferences) automatically.
Stateful Objects
Objects in level JSON can have a states array. Each state has enter/exit sounds, animations, and variable conditions. Advance state at runtime:
import { advanceObjectState, fireButtonTrigger, setLevelVar } from './core/stateManager.js';
advanceObjectState(myMesh); // cycle to next state
fireButtonTrigger(myMesh, 'press'); // fire a named trigger
setLevelVar('door_open', true); // set a level variableScripts
Level JSON can also carry scripts:
sceneScriptsfor level-wide modulesscriptson any placed object entry
Modules can export:
onLoad(ctx)onUpdate(ctx, delta)onStateChange(ctx, nextIdx, prevIdx)onUnload(ctx)
The editor stores these as plain module paths, one per line. Starter examples live in scripts/scene.js and scripts/object.js.
If you build your own main loop, call initLevelScripts() after loadLevel() and updateLevelScripts(delta) once per frame.
Platform
Kinetik targets:
- Desktop — Electron (Windows / Mac / Linux)
- Mobile — Capacitor (Android), with touch joystick via
mobileControls.js - Browser — Vite dev server for rapid iteration
