Plugin

etchasketch.js

The classic 1960s toy as a drop-in web plugin. Arrow keys spin the knobs, the pen follows on a red bezel with an aluminum-gray screen. Click a knob — or shake your phone — to clear.

npm install @goboldlyforward/etchasketch

Playground

Try every knob

Adjust the controls — the code block updates with the exact options to recreate this configuration. Hit Copy and paste into your project.

Settings

Screen color aluminum
Live code

Press (or WASD) to move the pen — the knobs spin to match. Hold two arrows for diagonals. Click either knob to clear.

Install

Drop it in

No build step, no framework — one stylesheet, one script, one container.

<link rel="stylesheet" href="path/to/etchasketch.css">
<script src="path/to/etchasketch.js"></script>

<div id="toy"></div>

<script>
  const sketch = new EtchASketch('#toy', {
    penColor:    '#1f1f1f',
    penWidth:    2,
    stepPx:      1.8,
    screenColor: '#b8b8a9',
  });

  // Live tuning — every option is hot-swappable.
  sketch.setOptions({ cursor: true });
</script>

new EtchASketch(target, options?) mounts the red bezel + knobs + canvas into target (a selector or element) and starts listening for arrow keys. Mount as many as you like on a page — each is its own instance.

Controls

What every input does

The left knob is the horizontal axis; the right knob is the vertical axis — just like the real toy.

InputEffect
/ DPen right; left knob spins clockwise
/ APen left; left knob spins counterclockwise
/ SPen down; right knob spins clockwise
/ WPen up; right knob spins counterclockwise
Hold two arrowsDiagonal — normalized so it's the same speed as a single arrow
Click either knobClear the screen (with shake animation)
Shake the phoneClear (via DeviceMotionEvent; iOS needs enableShake())
Mouse / touch dragDraw — only when cursor: true

Reference

Options

Pass these at construction, or pass any of them later via setOptions().

new EtchASketch('#toy', {
  penColor:          '#1f1f1f',  // CSS color of the line
  penWidth:          2,          // stroke width in CSS px
  stepPx:            1.8,        // pen pixels per frame per held arrow
  screenColor:       '#b8b8a9',  // background fill (defaults to aluminum gray)
  chrome:            true,       // render the red bezel + knobs (false = headless canvas)
  cursor:            false,      // allow mouse/touch drag to draw (off = classic mode)
  shakeToErase:      true,       // listen for devicemotion and clear on a hard shake
  shakeThreshold:    22,         // accelerometer magnitude required (m/s²)
  knobsClickToClear: true,       // click either knob to clear
  knobDegPerPx:      2.2,        // how fast each knob spins per pen pixel
});

Methods

sketch.clear();                        // wipe the screen + run the shake animation
sketch.setOptions({ ... });            // merge + apply (hot-swap any option)
sketch.getDataURL('image/png');        // PNG data URL of the current drawing
sketch.enableShake();                  // call from a user gesture on iOS to grant motion access
sketch.resetPenTo(x, y);               // move the pen without drawing a line
sketch.destroy();                      // tear down DOM + listeners

Recipes

Cursor mode

By default, only the keyboard moves the pen. That keeps the experience faithful to the original 1960s Magic Screen — and means mouse/touch input on the canvas does nothing. To enable click-drag and finger-drag drawing:

const sketch = new EtchASketch('#toy', { cursor: true });
// ...or toggle it later:
sketch.setOptions({ cursor: true });

When cursor: true, the canvas shows a crosshair cursor, the pen jumps to wherever the user presses, and dragging strokes a line that the knobs also follow.

Headless mode

Skip the red bezel + knobs entirely and paint the interaction model onto your own design:

new EtchASketch('#raw', { chrome: false });

Your host element gets just the canvas. Arrow keys still draw; cursor: true still enables drag drawing.

Phone shake to clear (iOS)

Android and desktop browsers fire devicemotion events without a permission grant — shake works out of the box. iOS 13+ requires a one-time grant tied to a user gesture:

document.getElementById('btn-enable').addEventListener('click', () => {
  sketch.enableShake();   // shows the iOS permission dialog
});