Interactive Lesson

Creating Simulations

mr-md embeds interactive JavaScript simulations natively within a secure, sandboxed iframe. These simulations can respond to user inputs, adapt to dark/light mode themes seamlessly, and are optimized for high-DPI displays.

Whether you're building a simple interactive diagram or a complex physics sandbox, mr-md provides a simple yet powerful API to get you up and running quickly.

Check out this interactive pathfinding simulation built with the mr-md API:

Interactive Lab
Click to interact
Pathfinder Simulation

1. Getting Started

Creating a simulation involves two pieces: a JavaScript file that contains your simulation logic, and a simple Markdown embed to display it.

Step 1: Write your Simulation Script

Create a new file called mysim.js (or .ts) in your project. We'll use the mr-md global API to draw a simple rectangle:

javascript
// mysim.js
window.bkSetup(800, 500, function(ctx, fit) {
  // Clear the canvas every frame
  ctx.clearRect(0, 0, fit.width, fit.height);
  
  // Use dynamically synced theme colors!
  ctx.fillStyle = window.bkColor("primary");
  
  // Draw something
  ctx.fillRect(10, 10, 50, 50);
});

Step 2: Embed in Markdown

Embed the simulation using the standard Markdown image syntax, pointing to your .js or .ts file:

markdown
![My First Simulation](./mysim.js)

That's it! mr-md automatically wraps your script in a sandbox, provisions a high-DPI canvas, and runs your render loop.


2. Configuration & Interactive UI (`.config.json`)

One of the most powerful features of mr-md simulations is the ability to expose interactive controls (sliders, toggles) directly to the reader without writing any UI code.

If you place a JSON file next to your script with the same name (e.g., mysim.config.json for mysim.js), mr-md will parse it, generate a UI block above the simulation, and pass the properties to your script at runtime.

Example: `mysim.config.json`

json
{
  "props": {
    "mazeDensity": 25,
    "diagonal": false
  },
  "tunables": {
    "mazeDensity": { 
      "label": "Maze Density (%)", 
      "min": 0, 
      "max": 50, 
      "step": 1 
    },
    "diagonal": { 
      "label": "Allow Diagonal", 
      "type": "boolean" 
    }
  }
}
  • props: The default values that are passed to your simulation.
  • tunables: Describes how mr-md should render the UI. A range slider will be generated for mazeDensity, and a toggle switch for diagonal.

Accessing Props in your Script

Inside your simulation script, you can access these values—which update in real-time as the user interacts with the UI—via the window.__simProps object:

javascript
window.bkSetup(800, 500, function(ctx, fit) {
  const density = window.__simProps.mazeDensity;
  const allowDiag = window.__simProps.diagonal;
  
  // Use 'density' and 'allowDiag' in your simulation logic!
});

3. The `bk` API Reference

To ensure simulations scale perfectly and match the host's theme seamlessly, mr-md injects several bk (BlogKit) global functions into the iframe sandbox.

`window.bkSetup(width, height, loopFn)`

Bootstraps a 2D canvas simulation.

  • It automatically creates an internal canvas.
  • It sets up DPI scaling for retina displays.
  • It handles window resizing dynamically.
  • It invokes your loopFn(ctx, fit) on every animation frame.

Tip: You don't need to write a requestAnimationFrame loop. bkSetup handles the run loop for you automatically!

`window.bkColor(name)`

Fetches a dynamically synced color from the mr-md theme system. If the user toggles dark mode on your site, these colors update automatically!

javascript
const primaryColor = window.bkColor("primary");
const bgColor = window.bkColor("background");
const textRed = window.bkColor("red");

`window.bkCanvasPoint(event, canvas)`

Translates raw DOM pointer coordinates (like event.clientX) into the logical coordinate space of the DPI-scaled canvas. Essential for handling mouse and touch interactions accurately.

javascript
// Access the underlying canvas created by bkSetup
const canvas = document.getElementById("c");

canvas.addEventListener("pointermove", (e) => {
  const pt = window.bkCanvasPoint(e, canvas);
  console.log("Mouse X:", pt.x, "Mouse Y:", pt.y);
});

`window.bkFitCanvas(canvas, requestedW, requestedH, options)`

If you are doing custom canvas rendering (like WebGL instead of the 2D context provided by bkSetup), call this manually to apply the CSS scaling transforms needed to make your canvas responsive inside the mr-md layout container.