Skip to content

[DMP 2026]: Expand plugin system and plugin API to allow more complex integrations #6611

@sum2it

Description

@sum2it

Ticket Contents

Description

Music Blocks has an existing plugin system that allows developers to add new palettes and blocks via JSON-encoded plugin files (.json), without modifying core code. Plugins can define new blocks (BLOCKPLUGINS), flow logic (FLOWPLUGINS), argument blocks (ARGPLUGINS), macros (MACROPLUGINS), palette colors, and lifecycle hooks (ONLOAD, ONSTART, ONSTOP).

However, the current system has significant limitations that prevent complex integrations:

eval()-based execution — Block and global code is executed via eval(), which is broken on Chrome due to Content Security Policy (CSP) restrictions (unsafe-eval not allowed). There is an existing FIXME comment in js/utils/utils.js acknowledging this.
No structured plugin API — Plugins access internals (e.g., globalActivity, logo, turtle) directly with no stable, versioned API surface. This makes plugins fragile against core changes.
No plugin-to-plugin communication — There is no mechanism for plugins to declare dependencies on or communicate with other plugins.
No access to the music synthesis pipeline — Plugins cannot hook into the Tone.js-based audio synthesis pipeline, the Singer component, or the Turtle system in a structured way.
No widget creation from plugins — Plugins cannot add new widgets (like Phrase Maker, Rhythm Maker, etc.) to the UI.
No plugin versioning or metadata — There is no schema for plugin version, author, description, or compatibility information.
No async block support — Plugins have no clean way to define blocks with asynchronous behavior (e.g., fetching data, waiting for hardware).
Limited tooling — The pluginify.py tool converts .rtp to .json but provides no validation, testing harness, or developer feedback.
This project aims to redesign and expand the plugin API to address these limitations, enabling richer, more reliable, and more complex integrations.

Goals & Mid-Point Milestone

Goals

  • Replace eval()-based plugin execution with a CSP-compliant mechanism (e.g., using Function constructor with a sandboxed scope, or ES module dynamic imports via blob: URLs)
  • Design and implement a stable, versioned plugin API object that exposes controlled access to activity, logo, Singer, Turtles, and the block/palette systems
  • Add plugin metadata schema (name, version, author, description, Music Blocks compatibility range) and a loader that validates it
  • Implement plugin-to-plugin dependency declaration and a basic dependency resolution mechanism
  • Add support for async blocks in plugins (blocks that can pause execution and resume after a Promise resolves)
  • Enable plugins to register custom widgets in the UI
  • Update pluginify.py (or create a new JS/Node.js-based tool) with validation, linting, and a developer test harness
  • Write comprehensive documentation and at least two example plugins demonstrating the new API capabilities
  • Goals Achieved By Mid-point Milestone:
  • CSP-compliant execution mechanism implemented and tested on Chrome and Firefox
  • Stable plugin API object designed and documented, with controlled access to core subsystems
  • Plugin metadata schema defined and enforced by the loader
  • At least one existing plugin (e.g., rodi.json) migrated to the new API as a proof of concept

Setup/Installation

Clone the repository: git clone https://github.com/sugarlabs/musicblocks.git
Install dependencies: npm install
Run a local instance: npm run serve
Load an existing plugin (e.g., plugins/rodi.json) via the Load Plugin button in the secondary toolbar to understand the current system
Read plugins/README.md for plugin authoring documentation
Read js/utils/utils.js (processPluginData, processRawPluginData, updatePluginObj) for the current plugin loading pipeline
Read js/activity.js (doPluginsAndPaletteCols, _deletePlugin) for plugin lifecycle management

Expected Outcome

A CSP-compliant plugin execution engine that works on Chrome, Firefox, and other modern browsers without requiring unsafe-eval
A documented, stable MusicBlocksPluginAPI object that plugins use instead of accessing globalActivity directly, providing controlled access to blocks, palettes, the Singer/Turtle music pipeline, and UI widgets
A plugin metadata format (JSON schema) that includes versioning and compatibility information, validated at load time
Support for async blocks in plugins, enabling hardware integrations (like the existing rodi robot plugin) and network-dependent blocks to work cleanly
The ability for plugins to register custom widgets alongside the built-in ones (Phrase Maker, Rhythm Maker, etc.)
An updated or new developer toolchain (pluginify tool) with validation and a test harness
At least two fully working example plugins demonstrating the new capabilities
All existing plugins (rodi.json, maths.json, nutrition.json) migrated to or verified compatible with the new API

Acceptance Criteria

All existing plugins load and function correctly under the new system on Chrome and Firefox
No use of bare eval() remains in the plugin loading pipeline
The plugin API is documented with JSDoc and a developer guide
New plugins can be authored using only the public API object without needing to reference globalActivity directly
Async blocks work correctly: execution pauses and resumes without breaking the Logo interpreter's flow
Plugin metadata is validated at load time; invalid or incompatible plugins show a clear error message
At least one plugin registers a custom widget successfully
All changes pass the existing ESLint configuration and CI checks

Implementation Details

Current plugin loading pipeline (files to modify):

js/utils/utils.js — processPluginData() (lines 575–790): replace eval() calls for BLOCKPLUGINS, GLOBALS, and ONLOAD with a CSP-compliant alternative
js/activity.js — doPluginsAndPaletteCols() (lines 606–666) and _deletePlugin() (lines 2058–2114): update plugin object structure to include metadata
plugins/pluginify.py — extend or replace with a Node.js tool that validates against the new schema
CSP-compliant execution approach:

Use new Function(...) with an explicit scope object passed as arguments, or
Use dynamic import() with blob: URLs for ES module plugins (modern approach)
Investigate using a Web Worker sandbox for plugin execution isolation
Plugin API object design:

// Proposed API surface exposed to plugins
const MusicBlocksPluginAPI = {
version: "1.0",
blocks: { register, addToPalette },
palettes: { add, update },
singer: { onNoteStart, onNoteEnd, registerInstrument },
turtles: { onTurtleStart, onTurtleStop },
widgets: { register },
i18n: { translate: _ },
storage: { get, set },
events: { on, emit }
};
Plugin metadata schema (new required field):

{
"METADATA": {
"name": "my-plugin",
"version": "1.0.0",
"author": "Developer Name",
"description": "What this plugin does",
"musicblocksVersion": ">=3.0"
}
}
Technologies: JavaScript (ES2020+), Node.js (for tooling), JSON Schema (for validation), Tone.js (for audio pipeline hooks), Web Workers (optional sandboxing)

Mockups/Wireframes

No response

Product Name

Music Blocks

Organisation Name

Sugar Labs

Domain

⁠Education

Tech Skills Needed

JavaScript

Mentor(s)

@sum2it @walterbender @omsuneri

Category

Frontend

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions