SkillAgentSearch skills...

Kernelplay

A lightweight JavaScript game core library for building games and engines.

Install / Use

/learn @Soubhik1000/Kernelplay
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

KernelPlayJS

A 2D/3D JavaScript game engine that feels like Unity — but lives in your browser. Built on an Entity–Component architecture, KernelPlayJS is fast, flexible, and surprisingly fun to use.

v0.2.3-alpha · MIT License · Built by Soubhik Mukherjee


<div align="left"> <img height="400" src="https://soubhik2.github.io/HomeLand.github.oi/Images/PerformanceM.png" /> </div>

🔴 Live Demo

👉 https://soubhik-rjs.github.io/kernelplay-js-demo/examples/Canvas2D/

🏁 Benchmark Demo · 📚 Full Documentation


⚡ Why KernelPlayJS?

Most browser game engines either hold your hand too much or leave you drowning in boilerplate. KernelPlayJS hits the sweet spot — it handles the hard stuff (physics [AABB], rendering [Canvas 2D, Pixi JS, Three JS], collision, object pooling) so you can focus on making your game actually fun.

  • 10,000+ objects at 60 FPS on a 7th gen i3 — yes, really
  • 3,000 physics objects with full collision detection at 40+ FPS
  • Spatial grid turns O(n²) collision into O(n) — automatic, no config needed
  • Frustum culling — skips anything off-screen entirely
  • Object pooling — spawn 1000+ bullets/sec with zero GC stutters
  • Dirty flag system — 91% fewer transform recalculations for static objects
  • Batch rendering — groups draws by color to cut canvas state changes by 100×

📦 Installation

npm install kernelplay-js

Or drop it straight into HTML with a CDN:

<script type="importmap">
{
  "imports": {
    "kernelplay-js": "https://cdn.jsdelivr.net/npm/kernelplay-js/dist/kernelplay.es.js"
  }
}
</script>

Optional Renderer Plugins

The core engine ships with Canvas 2D — zero external dependencies. When your game needs more visual firepower, bolt on a renderer plugin:

npm install @kernelplay/pixi-renderer    # Pixi.js — GPU-accelerated 2D sprites & effects
npm install @kernelplay/three-renderer   # Three.js — full 3D with lights, meshes, shadows

🎥 CameraComponent (New in v0.2.3)

No more hassle with manual game camera handling.

❌ Old Way (Manual Camera)

Previously, you had to manually sync the camera with the player:

// Camera follows player manually
this.camera.x = transform.position.x - this.camera.width / 2;
this.camera.y = transform.position.y - this.camera.height / 2;

✅ New Way (Camera as a GameObject)

Now, the camera is just another Entity in your scene.

class GameScene extends Scene {
  init() {
    // Create player
    const player = new Player(400, 300);
    this.addEntity(player);
    
    // Create camera entity
    const camera = new Entity("MainCamera");

    camera.addComponent("transform", new TransformComponent({
      position: { x: 400, y: 300, z: 0 }
    }));
    
    camera.addComponent("camera", new CameraComponent({
      width: this.game.config.width,
      height: this.game.config.height,

      // 🔥 Follow system
      target: player,
      followSpeed: 5,

      // Offset from target
      offset: { x: 0, y: -50, z: 0 },

      // Level bounds (prevents showing outside world)
      bounds: {
        minX: 0,
        maxX: 2000,
        minY: 0,
        maxY: 1500
      },

      isPrimary: true
    }));
    
    this.addEntity(camera);
  }
}

🎮 Using Camera in Scripts

export class PlayerController extends ScriptComponent {
  onStart() {
    // Get primary camera
    this.primaryCamera = this.entity.scene.getPrimaryCamera();
  }

  update(dt) {
    // Shortcut access
    this.camera;
  }
}

🧠 CameraComponent Methods

// Set follow target
this.camera.setTarget(this.entity);

// Screen shake effect
this.camera.shake(20, 0.5);

// Convert screen → world coordinates
const worldPos = this.camera.screenToWorld(Mouse.x, Mouse.y);

// Convert world → screen coordinates
const screenPos = this.camera.worldToScreen(pos.x, pos.y);

// Check if a position is visible
this.camera.isInView(x, y);

🎥 Multiple Cameras Setup

// Camera 1 (Primary)
const camera1 = new Entity("MainCamera");
camera1.id = 100;

camera1.addComponent("transform", new TransformComponent({
  position: { x: 400, y: 300, z: 10 }
}));

camera1.addComponent("camera", new CameraComponent({
  width: 800,
  height: 600,
  isPrimary: true
}));

// Camera 2
const camera2 = new Entity("Camera2");
camera2.id = 101;

camera2.addComponent("transform", new TransformComponent({
  position: { x: 0, y: 0, z: 10 }
}));

camera2.addComponent("camera", new CameraComponent({
  width: 800,
  height: 600,
  isPrimary: false
}));

🔄 Switching Between Cameras

// Inside ScriptComponent
this.setPrimaryCamera(this.camera2);

⚡ Prop Injection System

ScriptComponent now supports automatic prop injection.

You can directly pass references and values — no manual lookup needed.

🔗 Setup

import { ref } from "kernelplay-js";

player.addComponent("playerController", new PlayerController({
  enemy: ref(5),
  force: 800,
  camera1: ref(100),
  camera2: ref(101),
}));

🚀 Usage

// Use injected values directly
if (Keyboard.isPressed(KeyCode.ArrowRight)) {
  rb.addForce(this.force, 0);
}

// Switch camera
this.setPrimaryCamera(this.camera2);

// Destroy referenced entity
this.enemy.destroy();

// Change camera target
this.camera2.getComponent("camera").setTarget(this.enemy);

✨ Summary

  • Camera is now a full Entity
  • Built-in smooth follow system
  • Supports multiple cameras
  • Easy camera switching
  • Powerful prop injection system
  • Cleaner, modular, and scalable 🎯

🚀 Quick Start

import { Game, Scene, Entity, TransformComponent, BoxRenderComponent } from "kernelplay-js";

class MyScene extends Scene {
  init() {
    const box = new Entity();
    box.addComponent("transform", new TransformComponent({ position: { x: 300, y: 200 } }));
    box.addComponent("renderer", new BoxRenderComponent({ color: "red" }));
    this.addEntity(box);
  }
}

class MyGame extends Game {
  init() {
    this.sceneManager.addScene(new MyScene("Main"));
    this.sceneManager.startScene("Main");
  }
}

new MyGame({ width: 800, height: 600, fps: 60 }).start();

🎮 Core Concepts

Everything in KernelPlayJS is built around three ideas:

  • Entities — your game objects (player, bullet, enemy, tree)
  • Components — data attached to entities (position, physics, renderer)
  • Scripts — the brains; custom logic that runs every frame
export class Player extends Entity {
  constructor(x, y) {
    super("Player");
    this.tag = "player";
    this.zIndex = 10; // renders on top

    this.addComponent("transform", new TransformComponent({ position: { x, y } }));
    this.addComponent("rigidbody2d", new Rigidbody2DComponent({ mass: 1, gravityScale: 1 }));
    this.addComponent("collider", new ColliderComponent({ width: 50, height: 50 }));
    this.addComponent("renderer", new BoxRenderComponent({ color: "red" }));
    this.addComponent("controller", new PlayerController());
  }
}

🧠 Script Lifecycle

Scripts work just like Unity's MonoBehaviour — with clean hooks for every stage of an entity's life:

export class PlayerController extends ScriptComponent {

  onStart() {
    this.speed = 200;
    this.jumpForce = 500;
  }

  update(dt) {
    const rb = this.entity.getComponent("rigidbody2d");

    rb.velocity.x = 0;
    if (Keyboard.isDown(KeyCode.ArrowRight)) rb.velocity.x = this.speed;
    if (Keyboard.isDown(KeyCode.ArrowLeft))  rb.velocity.x = -this.speed;

    if (Keyboard.wasPressed(KeyCode.Space) && rb.isGrounded) {
      rb.velocity.y = -this.jumpForce;
    }

    if (Mouse.wasPressed(MouseButton.Left)) {
      this.instantiate(Bullet, this.transform.position.x, this.transform.position.y);
    }
  }

  onCollision(other) {
    if (other.tag === "enemy") this.takeDamage(10);
  }

  onDestroy() {
    // cleanup resources here
  }
}

Lifecycle order: onAttach → onStart → update → lateUpdate → onDestroy


🔫 Object Pooling (Automatic)

Spawning hundreds of bullets per second? KernelPlayJS silently recycles destroyed entities back into a pool so they can be reused — no setup, no pool sizes to configure, no GC spikes to fight.

// Creates a Bullet, or quietly reuses a destroyed one from the pool
this.instantiate(Bullet, x, y);

// When the bullet's lifetime ends or it hits something:
this.destroy(); // entity goes back to the pool, not the garbage collector

Bullet Prefab (Auto-pooled)

// Do not use Entity Object for the Bullet prefab if it will be instantiated.
// It now contains data only for ECS.

export function Bullet(entity, x = 100, y = 100) {
    entity.name = "Bullet";
    entity.tag = "bullet";

    entity.addComponent("transform", new TransformComponent({
        position: { x, y },
        scale: { x: 0.2, y: 0.2 }
    }));

    entity.addComponent("rigidbody2d", new Rigidbody2DComponent({
        mass: 1,
        gravityScale: 1,
        drag: 1,
        useGravity: false
    }));

    entity.addComponent("collider", new ColliderComponent({
        isTrigger: true
    }));

    entity.addComponent("renderer", new BoxRenderComponent({color:"#00ff11", zIndex:-20}));
    entity.addComponent("bulletscript", new BulletScript());
}

class BulletScript extends ScriptComponent {
  constructor(direction) {
    super();
    this.direction = direction;
    this.lifetime = 2.0; // seconds
  }

  update(dt) {
    this.transform.position.x += this.direction.x * 500 * dt;
    this.transform.position.y += this.direction.y * 500 * dt;
    
    this.lifetime -= dt;
    if (this.lifetime <= 0) {
      this.entity.destroy(); // Returns to pool automatically
    }
  }

  onTriggerEnter(other) {
    if (other.tag === "enemy") {
      othe
View on GitHub
GitHub Stars23
CategoryDevelopment
Updated2h ago
Forks0

Languages

JavaScript

Security Score

75/100

Audited on Apr 1, 2026

No findings