PlayCanvas Lite Runner
The lite runner requires you to load PlayCanvas separately before loading the runner. This results in a smaller Bitbybit bundle size and allows you to use your own version of PlayCanvas.
Live Example
Complete Example
Below is a complete example that creates a parametric lofted surface with rectangle holes using OCCT:
<!doctype html>
<html lang="en">
<head>
<title>Bitbybit Runner PlayCanvas Lite Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Load PlayCanvas separately first -->
<script src='https://code.playcanvas.com/playcanvas-stable.min.js'></script>
<script type="module">
window.PLAYCANVAS = pc;
</script>
<!-- Load the lite runner (requires PlayCanvas to be loaded) -->
<script defer
src="https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@latest/runner/bitbybit-runner-lite-playcanvas.js"></script>
<script type="module">
const runnerOptions = {
canvasId: 'myCanvas',
canvasZoneClass: 'myCanvasZone',
enableOCCT: true,
enableJSCAD: false,
enableManifold: false,
cameraPosition: [20, 10, 20],
cameraTarget: [10, 5, 0],
backgroundColor: "#1a1c1f",
loadFonts: ['Roboto'],
};
const runner = window.bitbybitRunner.getRunnerInstance();
const { bitbybit, Bit, camera, scene, app, pc } = await runner.run(
runnerOptions
);
// Create additional directional light
const dirLight = new pc.Entity("additionalLight");
dirLight.addComponent("light", {
type: "directional",
color: new pc.Color(1, 1, 1),
intensity: 2,
castShadows: true,
shadowBias: 0.2,
normalOffsetBias: 0.05,
shadowResolution: 2048
});
dirLight.setPosition(60, 70, -30);
dirLight.lookAt(0, 0, 0);
scene.addChild(dirLight);
// Model parameters
const model = {
uRec: 16,
vRec: 16,
rounding: 0.5,
drawEdges: true,
drawFaces: true,
color: '#6600ff'
}
// Create curve points for lofting
const curvePts = [
[[-10, 0, -10], [0, 3, -10], [10, -1, -10], [20, 2, -10]],
[[-10, -5, 0], [0, -3, 0], [10, 1, 0], [20, -2, 0]],
[[-10, 0, 10], [0, 3, 10], [10, -1, 10], [20, 2, 10]]
];
// Create wires from interpolated points
const wirePromises = curvePts.map((pts) => {
return bitbybit.occt.shapes.wire.interpolatePoints({
points: pts, periodic: false, tolerance: 1e-7
});
});
const wires = await Promise.all(wirePromises);
const loft = await bitbybit.occt.operations.loft({ shapes: wires, makeSolid: false });
const translated = await bitbybit.occt.transforms.translate({ shape: loft, translation: [0, 10, 0] });
const faces = await bitbybit.occt.shapes.face.getFaces({ shape: translated });
// Subdivide face with rectangle holes
const subdivideOptions = new Bit.Inputs.OCCT.FaceSubdivideToRectangleHolesDto(faces[0]);
subdivideOptions.nrRectanglesU = model.vRec;
subdivideOptions.nrRectanglesV = model.uRec;
subdivideOptions.scalePatternU = [0.9, 0.5, 0.7];
subdivideOptions.scalePatternV = [0.9, 0.5, 0.7];
subdivideOptions.filletPattern = [model.rounding];
subdivideOptions.inclusionPattern = [false, true, true, true, true];
subdivideOptions.offsetFromBorderU = 0.01;
subdivideOptions.offsetFromBorderV = 0.01;
const withHoles = await bitbybit.occt.shapes.face.subdivideToRectangleHoles(subdivideOptions);
const finalShape = await bitbybit.occt.operations.makeThickSolidSimple({
shape: withHoles[0], offset: 0.5
});
// Draw options
const options = new Bit.Inputs.Draw.DrawOcctShapeOptions();
options.precision = 0.02;
options.drawEdges = model.drawEdges;
options.drawFaces = model.drawFaces;
options.drawVertices = false;
options.edgeWidth = 20;
options.edgeColour = "#000000";
options.faceColour = model.color;
options.drawTwoSided = false;
const group = await bitbybit.draw.drawAnyAsync({ entity: finalShape, options });
</script>
<style>
body {
margin: 0;
background-color: #1a1c1f;
}
#myCanvas {
display: block;
width: 100%;
height: 100vh;
}
.myCanvasZone {
width: 100%;
height: 100vh;
}
</style>
</head>
<body>
<div class="myCanvasZone">
<canvas id="myCanvas"></canvas>
</div>
</body>
</html>
Key Points
Loading PlayCanvas First
<script src='https://code.playcanvas.com/playcanvas-stable.min.js'></script>
<script type="module">
window.PLAYCANVAS = pc;
</script>
Important: You must expose PlayCanvas to the window object as window.PLAYCANVAS before loading the lite runner.
Lite Runner Script
<script defer src="https://cdn.jsdelivr.net/gh/bitbybit-dev/bitbybit-assets@latest/runner/bitbybit-runner-lite-playcanvas.js"></script>
Note the defer attribute - this ensures the script loads after PlayCanvas is available.
Initialization
const runner = window.bitbybitRunner.getRunnerInstance();
const { bitbybit, Bit, camera, scene, app, pc } = await runner.run(runnerOptions);
The returned objects are the same as the full runner.
When to Use Lite vs Full
| Use Case | Recommended |
|---|---|
| Quick prototyping | Full Runner |
| Need specific PlayCanvas version | Lite Runner |
| Smallest possible bundle | Lite Runner |
| Using other PlayCanvas modules | Lite Runner |
| Simple single-page demos | Full Runner |
Runner Options
The options are the same as the full runner:
| Option | Type | Description |
|---|---|---|
canvasId | string | The ID of your canvas element |
canvasZoneClass | string | CSS class for the canvas container |
enableOCCT | boolean | Enable OpenCASCADE kernel |
enableJSCAD | boolean | Enable JSCAD kernel |
enableManifold | boolean | Enable Manifold kernel |
cameraPosition | number[] | Initial camera position [x, y, z] |
cameraTarget | number[] | Camera look-at target [x, y, z] |
backgroundColor | string | Scene background color (hex) |
loadFonts | string[] | Fonts to load for text operations |
GitHub Source
View the full source code on GitHub: PlayCanvas Lite Runner Examples
