indiecake-engine
v0.1.0
Published
Lightweight game engine for Indie Cake platform with 2D graphics, input handling, and audio support
Maintainers
Readme
Indie Cake Engine v0.1.0
A lightweight 2D runtime for Indie Cake microgames, with platform integration, TypeScript types, and CDN-ready builds.
Highlights
- Small production runtime (
dist/indiecake-engine.min.js, ~9 KB gzipped) - Multiple output formats (ESM, CJS, IIFE)
- Built-in systems for graphics, input, audio, assets, game state, platform events, and difficulty
- Type definitions generated in
dist/index.d.ts - Release-ready scripts for bundle-size checks and Subresource Integrity (SRI) hashes
Install
npm install indiecake-engineQuick Start
ESM (recommended)
import { IndieCake } from 'indiecake-engine';
IndieCake.init({
canvas: 'gameCanvas',
width: 800,
height: 600,
});
IndieCake.graphics.clear('#000');
IndieCake.graphics.drawRect(100, 100, 50, 50, { color: '#ff0000' });
if (IndieCake.input.isKeyPressed('Space')) {
IndieCake.platform.setScore(10);
}CommonJS
const { IndieCake } = require('indiecake-engine');
IndieCake.init({ canvas: 'gameCanvas' });Browser via jsDelivr
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/indiecake-engine.min.js"></script>
<script>
window.IndieCake.init({ canvas: 'gameCanvas' });
</script>CDN URLs
- Latest:
https://cdn.jsdelivr.net/npm/indiecake-engine@latest/dist/indiecake-engine.min.js - Pinned:
https://cdn.jsdelivr.net/npm/[email protected]/dist/indiecake-engine.min.js - Fallback:
https://unpkg.com/[email protected]/dist/indiecake-engine.min.js
Build Scripts
npm run build: production build (clean -> rollup -> size checks)npm run build:rollup: non-production rollup buildnpm run build:rollup:prod: production rollup buildnpm run dev: watch modenpm run check:sizes: gzip size validationnpm run generate:sri: generate SRI hashes and writedist/sri.json
Build Outputs
dist/indiecake-engine.min.js: IIFE, minified (primary CDN artifact)dist/indiecake-engine.iife.js: IIFE, non-minifieddist/indiecake-engine.esm.js: ESM, non-minifieddist/indiecake-engine.esm.min.js: ESM, minifieddist/indiecake-engine.cjs.js: CommonJS, non-minifieddist/indiecake-engine.cjs.min.js: CommonJS, minifieddist/index.d.ts: Type declarationsdist/*.map: source maps
Using SRI Hashes
Generate hashes:
npm run generate:sriThen use the generated value in script tags:
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/indiecake-engine.min.js"
integrity="sha384-..."
crossorigin="anonymous"
></script>Monorepo Notes
- In this repository, server build copies
dist/indiecake-engine.esm.jstoapps/server/public/uploads. - Root turbo build now enforces
indiecake-engine#buildbefore@indiecake/server#build. - The server copy script also has a fallback that builds the engine if the artifact is missing.
Engine Systems
Graphics
- Canvas rendering
- Rect/circle/text/image primitives
- Basic transforms
Input
- Keyboard/mouse/touch support
- Press/state checks and event hooks
Audio
- Sound/music playback through Web Audio API
Assets
- Async loading and retrieval for image/audio/json/text
Platform
- Completion events
- Score integration
- Platform-friendly helpers
Difficulty
- Difficulty level setters/getters
- Scaled game behavior helpers
API Reference
Core Engine
IndieCake.init(config); // Initialize all systems
IndieCake.destroy(); // Cleanup all systems
IndieCake.isInitialized(); // Engine initialized state
IndieCake.complete(success); // One-shot game completion
IndieCake.win(); // Alias for complete(true)
IndieCake.lose(); // Alias for complete(false)
IndieCake.random(min, max); // Utility random number
IndieCake.clamp(value, min, max); // Utility clamp
IndieCake.lerp(a, b, t); // Utility interpolation
IndieCake.distance(x1, y1, x2, y2);Graphics (IndieCake.graphics)
graphics.clear(color?); // Clear frame or fill background color
graphics.save(); // Save canvas state (styles/transforms)
graphics.restore(); // Restore last saved canvas state
graphics.drawRect(x, y, width, height, options?); // Draw rectangle
graphics.drawCircle(x, y, radius, options?); // Draw circle
graphics.drawLine(x1, y1, x2, y2, options?); // Draw line segment
graphics.drawText(text, x, y, options?); // Draw text label
graphics.drawImage(image, x, y, width?, height?, options?); // Draw sprite/image
graphics.resize(width, height); // Resize render surface
graphics.getCanvas(); // Get underlying canvas element
graphics.getContext(); // Get 2D context for custom drawing
graphics.getWidth(); // Current logical canvas width
graphics.getHeight(); // Current logical canvas heightInput (IndieCake.input)
input.isKeyPressed(key); // True while key is held
input.isKeyDown(key); // Alias of isKeyPressed
input.isMousePressed(button?); // True while mouse button is held
input.getMousePosition(); // Current pointer position { x, y }
input.getTouches(); // Active touch points [{ x, y, id }]
input.isTouching(); // Any active touches present
input.addEventListener((event) => { ... }); // Subscribe to keyboard/mouse/touch events
input.removeEventListener(callback); // Unsubscribe input listenerAssets (IndieCake.assets)
await assets.loadAsset(id, url, type); // Load one asset (image|audio|json|text)
await assets.loadAssets(assetList); // Load multiple assets in parallel
await assets.loadAssetsWithProgress(assetList, onProgress); // Batch load with progress callback
assets.get(id); // Get raw cached asset by id
assets.getImage(id); // Get image asset as HTMLImageElement
assets.getAudio(id); // Get audio asset as HTMLAudioElement
assets.getJSON(id); // Get parsed JSON asset
assets.getText(id); // Get text asset contents
assets.unloadAsset(id); // Remove one cached asset
assets.isLoaded(id); // Check if asset exists and is ready
// Platform-aware helpers
await assets.loadImage(filenameOrAlias); // Load uploaded asset by alias/filename
await assets.loadImageByHash(hash); // Load uploaded asset by content hash
await assets.preloadPlatformAssets(); // Warm cache with platform registry assets
assets.getByHash(hash); // Read cached asset by hashAudio (IndieCake.audio)
await audio.loadAudio(id, url); // Preload audio asset
audio.unloadAudio(id); // Remove audio from cache
audio.isLoaded(id); // Check audio readiness
audio.play(id, { volume, loop, delay }); // Play sound with options
audio.pause(id); // Pause one track
audio.stop(id); // Stop and reset one track
audio.stopAll(); // Stop all currently playing audio
audio.setMasterVolume(volume); // Set global output volume (0..1)
audio.getMasterVolume(); // Read current global volume
audio.setMuted(boolean); // Mute/unmute all audio
audio.isMuted(); // Read mute stateGame State (IndieCake.game)
game.start(); // Enter running state
game.stop(); // Enter stopped state
game.pause(); // Pause updates
game.resume(); // Resume from paused state
game.complete(); // Mark game as completed
game.isRunning(); // True when running
game.isPaused(); // True when paused
game.isStopped(); // True when stopped
game.isCompleted(); // True when completed
game.getState(); // Current state string
game.createScene(id); // Create scene container
game.setActiveScene(id); // Switch active scene
game.getScene(id); // Get scene by id
game.removeScene(id); // Remove scene and objects
game.createObject(template?); // Create game object
game.getObject(id); // Get object by id
game.removeObject(id); // Remove object by id
game.addObjectToScene(objectId, sceneId); // Move object into scene
game.checkCollision(objA, objB); // AABB collision test
game.getObjectsAt(x, y); // Objects under point
game.getAllObjects(); // All objects across scenes
game.getActiveObjects(); // Objects with active=true
game.getVisibleObjects(); // Objects with visible=truePlatform (IndieCake.platform)
platform.complete({ success, score?, timeElapsed?, metadata? }); // Send completion payload to host
platform.isGameCompleted(); // Prevent duplicate completion submissions
platform.setScore(score); // Set current score
platform.addScore(points); // Increment score
platform.getScore(); // Read current score
platform.getElapsedTime(); // Seconds since platform init/reset
platform.resetGameTime(); // Reset elapsed timer for new round
platform.setCallbacks({ onGameComplete, onScoreUpdate }); // Register host/game callbacks
platform.getPlatformInfo(); // UA/device capability info
platform.saveData(key, value); // Persist JSON in localStorage
platform.loadData(key); // Read JSON from localStorage
platform.removeData(key); // Delete persisted keyDifficulty (IndieCake.difficulty)
difficulty.setLevel(level); // Set level (clamped to 1..5)
difficulty.getLevel(); // Read current level
difficulty.getSettings(level?); // Full settings for specific/current level
difficulty.getCurrentSettings(); // Full settings for active level
difficulty.getSpeedMultiplier(); // Numeric speed scaling factor
difficulty.getTimeMultiplier(); // Numeric time scaling factor
difficulty.getSpeedScale(baseSpeed); // Scale a speed value by difficulty
difficulty.getTimeScale(baseTime); // Scale a time value by difficulty
difficulty.onLevelChange(callback); // Subscribe to level changes
difficulty.removeCallback(callback); // Unsubscribe callback
difficulty.isEasy(); // Level 1-2
difficulty.isMedium(); // Level 3
difficulty.isHard(); // Level 4
difficulty.isExpert(); // Level 5
difficulty.isCasual(); // Level 1
difficulty.isStandard(); // Level 2
difficulty.isChallenging(); // Level 3
difficulty.isLegendary(); // Level 5Time (IndieCake.time)
time.update(deltaMs?); // Update timing from custom delta or performance.now()
time.deltaTime; // Seconds since last frame
time.totalTime; // Total seconds since init
time.currentFPS; // Estimated FPS (smoothed/updated internally)
time.now; // Current performance.now() value
time.getDelta(); // Alias for deltaTime
time.getElapsed(); // Alias for totalTime
time.getFPS(); // Alias for currentFPSCommon Usage Patterns
1) Basic game loop with engine time
function tick() {
IndieCake.time.update();
const dt = IndieCake.time.deltaTime;
// update your game state using dt
// render via IndieCake.graphics
requestAnimationFrame(tick);
}
tick();2) Complete a game safely once
// IndieCake.complete() is one-shot per session
if (playerWon) {
IndieCake.win();
} else {
IndieCake.lose();
}3) Load assets with progress
await IndieCake.assets.loadAssetsWithProgress(
[
{ id: 'player', url: '/sprites/player.png', type: 'image' },
{ id: 'music', url: '/audio/theme.mp3', type: 'audio' },
],
(p) => console.log(`Loading ${p.loaded}/${p.total} (${p.percentage.toFixed(0)}%)`)
);4) Tune gameplay by difficulty level
const level = IndieCake.difficulty.getLevel();
// Option A: Direct multiplier-based scaling
const enemySpeed = 140 * IndieCake.difficulty.getSpeedMultiplier();
const timeLimit = 20 * IndieCake.difficulty.getTimeMultiplier();
// Option B: Use built-in scaling helpers
const spawnIntervalMs = IndieCake.difficulty.getTimeScale(1200);
const projectileSpeed = IndieCake.difficulty.getSpeedScale(220);
// Optional named checks for branching behavior
if (IndieCake.difficulty.isLegendary()) {
// Add advanced patterns, extra enemies, etc.
}
console.log('Difficulty level:', level);Difficulty cheat sheet
| Level | Label | Good for | Helpful checks |
| --- | --- | --- | --- |
| 1 | Casual | New players, generous timing, slower pacing | isCasual(), isEasy() |
| 2 | Standard | Default balance for most games | isStandard(), isEasy() |
| 3 | Challenging | Tighten timing and speed a bit | isChallenging(), isMedium() |
| 4 | Expert | Faster reactions, less forgiveness | isHard() |
| 5 | Legendary | Fastest pace and shortest time windows | isLegendary(), isExpert() |
For scaling, a good default pattern is:
- Use
getTimeScale(baseTime)when you want harder modes to reduce or stretch timing in a consistent way. - Use
getSpeedScale(baseSpeed)when you want movement or enemy speed to respond to the selected level. - Use
getCurrentSettings()when you need the full label, description, color, and multiplier set.
Configuration Example
IndieCake.init({
canvas: 'gameCanvas',
width: 800,
height: 600,
backgroundColor: '#000000',
fps: 60,
enableAudio: true,
enableInput: true,
enableGraphics: true,
enablePlatformIntegration: true,
debug: false,
});Release Flow (Repository)
- Bump version in
package.json - Run
npm run build - Create GitHub release tag (
v0.1.0style) - GitHub Actions publishes package to npm
- jsDelivr serves the new version automatically
License
MIT. See LICENSE.
