Open-source · TypeScript · built on Google Blockly

One definition, multiple representations.

Morphic Blocks is an embeddable TypeScript library built on top of Google Blockly. One block model renders in any number of custom representations, from icons to real source code.

npm install morphic-blocks
View on npm →
Same statement, three modes
SimpleThe Print block in simple mode: a printer icon, the label Print, and a one line description
PseudoThe Print block in pseudo mode: a block labelled Output with an empty input slot
Syntax-JSThe Print block in syntax-js mode: console.log with an empty argument

One block, many modes

A single morphic block carries several visual elements: icons, natural-language labels, and code templates. Modes decide which are shown, and switch at runtime without remounting.

Declarative & config-driven

Blocks in JSON, behaviors in TypeScript, and one CSS file per mode. An environment is extended or restyled by editing config, not through Blockly's multi-file imperative setup.

Built for transition

The same program can be shown as icons, blocks, or text, scaffolding the gradual path from block-based to text-based programming, with one framework across many learning stages.

No external requests

The framework never contacts another server. So the site alone decides which requests a browser makes during a visit, Blockly's images and sounds included: they load from the same site by default.

Quick start

Install the package, pass in the definitions and behaviors, mount.

That is the whole loop. The docs cover modes, toolbox, codespace, and behaviors in depth.

Read the full guide →
main.ts
import { MorphicBlocks } from "morphic-blocks";
import definitions from "./definitions.json";
import { behaviors } from "./behaviors";

// The constructor takes the whole definitions file plus your behaviors.
const engine = new MorphicBlocks(definitions, behaviors);

// One call sets up every view it gets a container for.
engine.mount({
  workspaceContainer: document.getElementById("workspace")!,
  toolboxContainer: document.getElementById("toolbox")!,
});

// Switch representation at runtime; the same blocks re-render.
engine.setModes({ workspaceMode: "python", toolboxMode: "python" });
definitions.json
{
  "elementTypes": {
    "title":      "text",
    "conceptual": "code",
    "python":     "code"
  },
  "modes": [
    { "name": "conceptual", "elements": ["title", "conceptual"] },
    { "name": "python",     "elements": ["title", "python"] }
  ],
  "blocks": [
    {
      "identifier": "text_print",
      "elements": {
        "title":      "Print",
        "conceptual": "Output %1",
        "python":     "print(%1)"
      },
      "inputSlots": {
        "1": { "kind": "value", "name": "TEXT" }
      }
    }
  ]
}
behaviors.ts
import type { MorphicBehaviorMap } from "morphic-blocks";

// One function per block identifier, returning the executable JavaScript.
export const behaviors: MorphicBehaviorMap = {
  text_print(proxy) {
    return `console.log(${proxy.inputs.TEXT ?? "undefined"});\n`;
  },
};

See it in action

Try the playground
The editor in its hybrid preset: a toolbox with blocks and their Python lines, a block workspace in pseudo mode, the same program as editable Python and read only JavaScript beside it, and the program's output below
Toolbox, workspace and two text views of one program