npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

scratchblocks-plus

v2.2.0

Published

Make pictures of Scratch blocks from text.

Readme

Make pictures of Scratch blocks from text.

Try it out!

Documentation


scratchblocks-plus is a fork of scratchblocks, and adds the following features:

Compatibility with scratchblocks

scratchblocks-plus follows the core text syntax and common browser APIs of scratchblocks 3.x, including parse, render, renderMatching, and loadLanguages. Most browser integrations can migrate by changing the package or script name.

It is not a complete package-level drop-in replacement:

  • scratchblocks-plus is ESM-first and does not support CommonJS require();
  • the root package entry is browser-only because it uses window and the DOM;
  • Node.js rendering and syntax-only parsing use dedicated package subpaths;
  • internal modules and exact generated SVG markup are not compatibility guarantees.

The classic script build still creates window.scratchblocks, so existing browser code that uses the documented scratchblocks API generally remains compatible.


scratchblocks-plus is used to write Scratch scripts:

It's MIT licensed, so you can use it in your projects.

For the full guide to the syntax, see the wiki.

Usage

ESM (recommended)

Install the package from npm:

npm install scratchblocks-plus

The root entry is intended for browser applications and bundlers. It default-exports the initialized scratchblocks API and automatically adds the required styles to the page:

import scratchblocks from "scratchblocks-plus"

scratchblocks.renderMatching("pre.blocks", {
  style: "scratch3",
  languages: ["en"],
  // catHats: true,
  // fontFamily: '"Noto Sans SC", sans-serif',
})

Set catHats to true to render all Scratch 3 hat and custom block definition hats as cat hats. It defaults to false and has no visual effect with the scratch2 style.

Use fontFamily to override the font family for all block labels, input values, and comments in that render. The value uses CSS font-family syntax; load the font before rendering, for example by waiting for document.fonts.ready. If the option is omitted or empty, each style keeps its default fonts.

The ESM entry does not create window.scratchblocks. Import the default export wherever it is needed.

To load every bundled locale in an ESM application:

import scratchblocks from "scratchblocks-plus"
import locales from "scratchblocks-plus/locales/all"

scratchblocks.loadLanguages(locales)

The all-locales entry is large. Import individual locale JSON files when your bundler supports JSON modules and you only need a few languages.

Node.js rendering

Use the dedicated SSR entry instead of the browser root. Install a DOM and canvas implementation alongside scratchblocks-plus:

npm install scratchblocks-plus @xmldom/xmldom @napi-rs/canvas
import { renderToSVGString } from "scratchblocks-plus/node-ssr"

const svg = renderToSVGString("move (10) steps", {
  style: "scratch3",
  fontFamily: '"Noto Sans SC", sans-serif',
})

For custom fonts in Node.js, register the font with the selected Canvas implementation before rendering so SVG layout measurement uses the same font.

Syntax-only parsing

Parsing and document analysis do not require a DOM or canvas implementation:

import { parse } from "scratchblocks-plus/syntax"

const document = parse("move (10) steps")
const block = document.scripts[0].blocks[0]

console.log(block.info.id) // "MOTION_MOVESTEPS"

Custom extensions

Register a complete extension before parsing its blocks:

import scratchblocks from "scratchblocks-plus"

const extension = scratchblocks.registerExtension({
  id: "demo",
  name: "Demo",
  category: {
    styles: {
      scratch3: {
        primary: "#0fbd8c",
        secondary: "#0da57a",
        tertiary: "#0b8e69",
      },
    },
  },
  blocks: {
    greet: {
      spec: "hello %1",
      aliases: ["greet %1"],
      inputs: ["%s"],
      shape: "stack",
    },
  },
  translations: {
    zh_cn: {
      greet: { spec: "你好 %1", aliases: ["问候 %1"] },
    },
  },
})

console.log(extension.blockIds) // ["demo.greet"]
const doc = scratchblocks.parse("hello [world]")

The extension ID is also its category name. Block keys are local opcodes; the API generates extensionId.opcode IDs and registers each spec and its aliases in English. IDs and opcodes accept letters, digits, underscores and hyphens. Each spec or alias must contain every numbered input exactly once (%1 through %9); translations can reorder them. Inputs use the existing types, such as %s, %n, %b, %c, %m, and %d.note. Supported shapes are stack, cap, hat, cat, reporter, boolean, ring, c-block, and c-block cap; loops may also set hasLoopArrow: true.

Translations accept a spec string or { spec, aliases }. Language codes are matched case-insensitively, treating _ and - alike. Load the normal language pack with loadLanguages() before parsing in that language. Extension translations are saved even if that pack is not yet loaded, and are reapplied when a fresh pack replaces it. Define English on the blocks, not in translations.en.

category.aliases adds names usable in :: category overrides. Category colors must be three- or six-digit hex colors. Styles may include scratch2 (one color), scratch3, scratch3HighContrast, and scratch3Outline (each a { primary, secondary, tertiary } palette). Omitted styles get usable defaults: Scratch 2 uses the primary color, high contrast uses the Scratch 3 palette, and outline uses white with its tertiary border color. Supply a separate high contrast palette when needed.

Add an icons array using the existing RegisterIconOptions format, then reference an icon's name in category.icon, or set inline: true and use @iconName in a spec. Icon names contain letters only and are globally unique; prefix them with your extension name to avoid collisions. If only Scratch 2 or Scratch 3 artwork is supplied, it is reused for the other renderer. A high contrast variant must use the same dimensions and vertical offset as its Scratch 3 artwork.

// Spread these fields into your extension manifest.
const iconOptions = {
  icons: [{
    name: "demoIcon",
    inline: true,
    scratch3: {
      width: 40,
      height: 40,
      source: { type: "image", data: "data:image/png;base64,..." },
    },
  }],
  category: { icon: "demoIcon" },
}

registerExtension() validates the entire manifest before registering any definitions, rejects duplicate extension/category/block/icon IDs, and refreshes installed page styles once. Existing SVGs must be rendered again by the caller. Registries remain shared within one loaded library module; reinitializing the renderer does not create an isolated registry. Unloading and replacement are not supported. Existing registerIcon, registerCategory, registerBlock, and registerBlockTranslation calls remain available.

Node.js uses the same manifest and returns the same registration result:

import {
  registerExtension,
  renderToSVGString,
} from "scratchblocks-plus/node-ssr"

registerExtension(myExtensionManifest)
const svg = renderToSVGString("hello [world]")

This API accepts extension metadata only. If an application reads TurboWarp extensions or another external format, it must obtain and convert that metadata outside the library, then pass the resulting manifest to registerExtension(). The example site's existing TurboWarp loader does this conversion before calling the API.

TypeScript model names are available as type-only exports from the browser entry:

import scratchblocks, {
  type Block,
  type Document,
} from "scratchblocks-plus"

const document: Document = scratchblocks.parse("move (10) steps")
const block: Block = document.scripts[0].blocks[0]

block.info.id identifies the registered Scratch block definition. For an application-defined identity, assign id directly to a Script, Block, Input, or Comment before creating a view or rendering:

const document = scratchblocks.parse("move (10) steps")
const block = document.scripts[0].blocks[0]
block.id = "workspace-block-42"

const view = scratchblocks.newView(document, { style: "scratch3" })
view.render()
const element = view.getElementById("workspace-block-42")

The ID is rendered as data-sb-id; it is not part of scratchblocks text and is not included by stringify(). Duplicate IDs are allowed, and lookup returns the first matching element in document order.

To hide a block without removing its layout space, set its optional hidden property before creating the view:

const doc = scratchblocks.parse(`repeat (10)
  move (10) steps
end`)
doc.getBlockByPath("1.1").hidden = true

const view = scratchblocks.newView(doc, { style: "scratch3" })
view.render()

When hidden is true, the block, its nested blocks, and attached visual annotations are invisible, while their original dimensions and positions are preserved. Hidden blocks remain available through element lookup APIs. The property is application-only: scratchblocks text cannot set it, and stringify() does not include it. Create a new view after changing hidden on an existing document.

React

Use the scratchblocks-plus-react package to render scratchblocks in React.

Classic HTML script

ESM is recommended for new projects. The classic IIFE build remains available for pages that use a global window.scratchblocks object.

You'll need to include a copy of the scratchblocks-plus JS file on your webpage. There are a few ways of getting one:

  • You could clone this repository and build it yourself using Node 16.14.0+ (npm run build).
<script src="scratchblocks-plus.min.js"></script>

The convention is to write scratchblocks inside pre tags with the class blocks:

<pre class="blocks">
when flag clicked
move (10) steps
</pre>

You then need to call scratchblocks.renderMatching after the page has loaded. Make sure this appears at the end of the page (just before the closing </body> tag):

<script>
  scratchblocks.renderMatching("pre.blocks", {
    style: "scratch3", // Optional, defaults to "scratch2".
    languages: ["en", "de"], // Optional, defaults to ["en"].
    scale: 1, // Optional, defaults to 1.
  })
</script>

The renderMatching() function takes a CSS-style selector for the elements that contain scratchblocks code: we use pre.blocks to target pre tags with the class blocks.

The style option controls how the blocks appear. Supported built-in styles are scratch2, scratch3, scratch3-high-contrast, and scratch3-outline.

Inline blocks

You might also want to use blocks "inline", inside a paragraph:

I'm rather fond of the <code class="b">stamp</code> block in Scratch.

To allow this, make a second call to renderMatching using the inline argument.

<script>
  scratchblocks.renderMatching("pre.blocks", ...)

  scratchblocks.renderMatching("code.b", {
    inline: true,
    // Repeat `style` and `languages` options here.
  })
</script>

This time we use code.b to target code blocks with the class b.

Translations

If you want to use languages other than English, you'll need to include a second JS file that contains translations. The releases page includes two options; you can pick one:

  • translations.js includes a limited set of languages, as seen on the Scratch Forums
  • translations-all.js includes every language that Scratch supports.

The translations files are hundreds of kilobytes in size, so to keep your page bundle size down you might like to build your own file with just the languages you need.

For example, a translations file that just loads the German language (ISO code de) would look something like this:

scratchblocks.loadLanguages({
    de: <contents of locales/de.json>
})

With ESM, import the locale JSON file when your bundler supports JSON modules:

import de from "scratchblocks-plus/locales/de.json"

scratchblocks.loadLanguages({
  de,
})

Languages

To update the translations:

npm upgrade scratch-l10n
npm run locales

Adding a language

Each language requires some additional words which aren't in Scratch itself (mainly the words used for the flag and arrow images). I'd be happy to accept pull requests for those! You'll need to rebuild the translations with npm run locales after editing the aliases.

Development

This should set you up and start a http-server for development:

npm install
npm start

Then open http://localhost:8000/ :-)

For more details, see CONTRIBUTING.md.

Credits

Many, many thanks to the contributors!

  • Maintained by LuYifei2011
  • This is a fork of scratchblocks, so all the credit there still applies here.
  • Original scratchblocks library by tjvr
  • Original scratchblocks library maintained by tjvr and apple502j
  • Icons derived from Scratch Blocks (Apache License 2.0)
  • Scratch 2 SVG proof-of-concept, shapes & filters by as-com
  • Anna helped with a formula, and pointed out that tjvr can't read graphs
  • JSO designed the syntax and wrote the original Block Plugin
  • Help with translation code from joooni
  • Block translations from the scratch-l10n repository
  • Ported to node by arve0