Jonathan Olson

Scenery is the 2D rendering engine (scene graph) used by PhET Interactive Simulations.

Embedded example of a PhET simulation using Scenery

I am the primary author of Scenery (started in late 2012), and I use it for the majority of my personal and professional projects for its fast and robust development of interactive graphics.

Key Strengths

  • Accessibility, internationalization, and color themes made easy
  • Declarative, agnostic to renderer
  • Can seamlessly split the scene graph into layers with mixed renderers (Canvas in front, SVG in back, embedding DOM content, etc.)
  • More powerful input handling than the DOM (e.g. expanded touch areas, more control over multi-touch)
  • Advanced declarative layout system
  • Comes with batteries included: Has a UI component library
  • Battle-tested by PhET, with many workarounds for browser quirks/bugs
  • Integrated 3D support with three.js

Key Weaknesses

Scenery has followed the constraints of PhET projects, so it has some limitations that may affect its use in other projects:

  • Standalone JS library is well supported, but JS-only and is 1.1MB minified.
  • Third-party use is not mature. NPM module (phet-lib) exists, but is only tested with Vite.
  • Newer versions need fairly up-to-date browser requirements (updated Chrome/Firefox, iOS 15+ / macOS 10.15+ Safari, etc.)
  • Only partial WebGL support (images/sprites/rectangles, including support for custom shaders). WebGPU support is planned.
  • Somewhat memory-heavy, especially for large scenes.
  • Best development experience is in TypeScript, many "input" type checks have been removed from debug builds.

Accessibility

Most ways of creating interactive graphics (e.g. Canvas, SVG, WebGL) are not inherently accessible, and many times user-hostile.

Scenery handles accessibility as a first-class citizen, with built-in support for screen readers, keyboard navigation, interactive highlights, sonification, and voicing.

Scenery creates a Parallel DOM structure that is invisible, but mirrors the visual content:

Parallel DOM created by Scenery

This allows screen readers to access the content as if it was built with DOM elements, and allows for normal keyboard navigation.

Scenery has built-in support to add descriptions and accessible names to everything in the scene graph, and tools to view and develop this content, with the ability to view the parallel DOM:

Accessible View of a PhET simulation (simulation on the left, parallel DOM on the right)
Click to explore the accessible view of a PhET simulation

Included above are the displayed views of aria-live (read-aloud) contextual and object responses, so that users can get feedback on their actions, and understand the state of the application.

It also supports customizable highlighting and controls for interactive elements:

Interactive highlight around a balloon, showing additional controls, in a PhET simulation

Input

Scenery has a powerful input system that is similar to the typical DOM event dispatch, but provides many extensions and customizations for interactive graphics.

It supports customizing the region of an element that is interactive, and is particularly useful for expanding the area that will respond to user touches on mobile/touchscreens:

Highlighted expanded touch areas on a PhET simulation control panel
Touch areas in dashed red, mouuse areas in dashed blue

It has the flexibility to implement multi-touch pan/zoom that can interrupt other presses, swipe-to-drag (for when the user might miss the initial object on a drag), "select/drag the closest item" (allows manual starting of presses or drags), and seamless dragging of items out of "toolboxes".

Rendering

Each displayable object in Scenery has a list of renderers (Canvas, SVG, WebGL, etc.) that can be used to render it. Scenery can be told which renderer to use, either for everything or for an entire branch of the scene graph.

Example displayed with Canvas only
Example displayed with SVG only

Scenery will look at what renderers are used for objects, and will combine front-to-back adjacent objects with the same renderer into a single layer (e.g. a single SVG or Canvas element). This allows mixing/layering things like a three.js 3D view with SVG content in the front AND back seamlessly and effortlessly.

It has a number of advanced optimizations to do this in a high-performance way, with a minimal amount of movement of objects between layers:

Example logic for changing layers with added/removed Canvas/SVG content
Example logic for changing layers with added/removed Canvas/SVG content

Components

The Sun library contains a large number of UI components built on top of Scenery, used in PhET simulations.

Some examples of Sun components (used in the Slitherlink app):

Example logic for changing layers with added/removed Canvas/SVG content
Example logic for changing layers with added/removed Canvas/SVG content

The components can have their styling and behavior customized, and are set up to work seamlessly with Scenery layout.

Layout

Scenery has a powerful layout system that can be used to declaratively lay out objects in a scene.

The Layout Documentation covers the basics, but it includes layout similar to CSS flexbox / grid, and more! Layout containers can be nested, and the preferred size of a component can be adjusted, allowing fluid layouts.

Try it out!

See the instructions for getting started with Scenery and phet-lib (phet-lib is a library that includes Scenery, the Sun components, and other associated code).

I would recommend checking out the demo using phet-lib with Vite, which allows for a quick start with a modern build system, and has the benefit of using TypeScript.