EyeWire II · Writing scripts

Add your own buttons, shortcuts and readouts to the game, in your own browser.

A script is a small piece of code that changes the game on your computer only. Nobody else sees it, and it does not need anyone's approval. Scripts run through a free browser extension called Tampermonkey, the same one players used on the original EyeWire.

Only install scripts you trust. A script runs as you. It can do anything you can do in the game, including editing cells with your sign in. Scripts you write yourself are fine. Read a script from someone else before you install it, or ask in chat whether others use it.

1. Install Tampermonkey

  1. Add Tampermonkey from the Tampermonkey site. It links to the right store for your browser.
  2. In Chrome or Edge, open chrome://extensions, click Details on Tampermonkey, and switch on Allow User Scripts. On older versions the switch is Developer mode, at the top right of that page. Without it, scripts do nothing and give no error.

2. Add your first script

  1. Click the Tampermonkey icon, then Create a new script.
  2. Delete everything in the editor and paste the script below.
  3. Press Ctrl+S to save, then reload the game.

A Layers? button appears at the bottom left. Click it to list the layers in your view.

// ==UserScript==
// @name         EyeWire II: list my layers
// @match        https://eyewire-ii-community-dot-brain-wire-dot-seung-lab.ue.r.appspot.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';
  // The game takes a few seconds to start, so wait for the viewer.
  const wait = setInterval(() => {
    if (!window.viewer) return;
    clearInterval(wait);

    const btn = document.createElement('button');
    btn.textContent = 'Layers?';
    btn.style.cssText = 'position:fixed;bottom:12px;left:12px;z-index:9999;padding:6px 10px;';
    btn.onclick = () => {
      const state = window.viewer.state.toJSON();
      alert((state.layers || []).map(l => `${l.name} (${l.type})`).join('\n'));
    };
    document.body.appendChild(btn);
  }, 1000);
})();

How a script is put together

Example: live position readout

Shows your current x, y, z at the bottom of the screen and updates as you move.

// ==UserScript==
// @name         EyeWire II: position readout
// @match        https://eyewire-ii-community-dot-brain-wire-dot-seung-lab.ue.r.appspot.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';
  const wait = setInterval(() => {
    if (!window.viewer) return;
    clearInterval(wait);

    const box = document.createElement('div');
    box.style.cssText = 'position:fixed;bottom:12px;left:12px;z-index:9999;padding:6px 10px;'
      + 'border-radius:6px;background:rgba(8,14,28,.9);color:#dce6f5;font:12px monospace;';
    document.body.appendChild(box);

    const show = () => {
      const p = window.viewer.state.toJSON().position || [];
      box.textContent = p.map(n => Math.round(n)).join(', ');
    };
    show();
    window.viewer.state.changed.add(show);
  }, 1000);
})();

Example: copy your selected cell IDs

Press Shift+C to copy the IDs of every cell selected in your view. It does nothing while you are typing in a box.

// ==UserScript==
// @name         EyeWire II: copy selected cell IDs
// @match        https://eyewire-ii-community-dot-brain-wire-dot-seung-lab.ue.r.appspot.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';
  function selectedCells() {
    const layers = window.viewer.state.toJSON().layers || [];
    return layers.filter(l => l.type === 'segmentation')
      .flatMap(l => l.segments || [])
      .filter(id => !String(id).startsWith('!'));   // "!" marks a hidden cell
  }

  window.addEventListener('keydown', e => {
    if (!window.viewer || !e.shiftKey || e.code !== 'KeyC') return;
    if (e.target.closest('input, textarea, [contenteditable]')) return;
    const ids = selectedCells();
    navigator.clipboard.writeText(ids.join(', '));
    console.log(`Copied ${ids.length} cell ID(s)`);
  });
})();

The eyewire API

The game gives scripts a small set of tools at window.eyewire. These are the parts we promise to keep working when the game updates. This is version 1.

CallWhat it does
eyewire.cells.selected()IDs of the cells selected in your view.
eyewire.cells.atCrosshair()The cell under the crosshairs. Returns a promise of { position, supervoxel, root }.
eyewire.cells.mine()Your claimed and completed cells in this dataset.
eyewire.view.get() / .set(state)Read or replace the whole view: layers, selected cells, position, annotations.
eyewire.view.position() / .goTo([x, y, z])Read or move the crosshairs.
eyewire.dataset()The dataset on screen: { id, label, layer }.
eyewire.user()Who is signed in: { id, username }, or null.
eyewire.on(event, fn)Run your function when something happens. Returns a function that stops listening.
eyewire.ui.addButton({ id, label, title, onClick })Add a button to the top bar. Returns { el, remove }.
eyewire.ui.addPanel({ id, title, open })Add a side panel. Put your content in el. Returns { el, open, close, toggle, isOpen, remove }.
eyewire.annotations.addLayer(name, { color })Make (or reuse) an annotation layer. Returns { addPoint, addLine, list, removeAnnotation, remove }.
eyewire.settings(name)A place to save your script's settings in this browser: { get, set, all, clear }.
eyewire.viewerThe viewer itself, for anything not covered here. This part can change.

Events

EventWhenWhat your function gets
claimYou claim a cell.{ taskId, cellId, dataset }
releaseYou release a claim.{ taskId, cellId, dataset }
completeYou complete a cell.{ taskId, cellId, dataset }
cellschangeThe selected cells change.{ selected, added, removed }
datasetchangeYou switch datasets.{ dataset }
viewchangeAnything in the view changes, such as moving.{ position }

Annotations your script adds are not counted in the Annotations number on your profile.

Example: a panel of your selected cells

Adds a Cells button to the top bar. It opens a side panel that lists your selected cell IDs and stays up to date.

// ==UserScript==
// @name         EyeWire II: selected cells panel
// @match        https://eyewire-ii-community-dot-brain-wire-dot-seung-lab.ue.r.appspot.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';
  // The game takes a few seconds to start, so wait for the API.
  const wait = setInterval(() => {
    if (!window.eyewire) return;
    clearInterval(wait);
    const ew = window.eyewire;

    const panel = ew.ui.addPanel({ id: 'selected-cells', title: 'Selected cells', open: false });
    const draw = () => {
      const ids = ew.cells.selected();
      panel.el.textContent = ids.length ? ids.join('\n') : 'No cells selected.';
      panel.el.style.whiteSpace = 'pre';
    };
    draw();
    ew.on('cellschange', draw);

    ew.ui.addButton({ id: 'selected-cells', label: 'Cells', title: 'Show selected cell IDs', onClick: panel.toggle });
  }, 1000);
})();

Example: count your completions

Shows how many cells you have completed on this computer. Click the button to reset it.

// ==UserScript==
// @name         EyeWire II: count my completions
// @match        https://eyewire-ii-community-dot-brain-wire-dot-seung-lab.ue.r.appspot.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';
  const wait = setInterval(() => {
    if (!window.eyewire) return;
    clearInterval(wait);
    const ew = window.eyewire;
    const saved = ew.settings('completion-counter');

    const btn = ew.ui.addButton({
      id: 'completion-counter',
      label: 'Done: ' + saved.get('count', 0),
      title: 'Cells you completed on this computer. Click to reset.',
      onClick: () => { saved.set('count', 0); btn.el.textContent = 'Done: 0'; },
    });

    ew.on('complete', cell => {
      const n = saved.get('count', 0) + 1;
      saved.set('count', n);
      btn.el.textContent = 'Done: ' + n;
      console.log('Completed cell', cell.cellId, 'in', cell.dataset);
    });
  }, 1000);
})();

Example: mark where you are

Adds a Mark button that drops a gold point at the crosshairs, in its own layer.

// ==UserScript==
// @name         EyeWire II: mark where I am
// @match        https://eyewire-ii-community-dot-brain-wire-dot-seung-lab.ue.r.appspot.com/*
// @grant        none
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';
  const wait = setInterval(() => {
    if (!window.eyewire) return;
    clearInterval(wait);
    const ew = window.eyewire;

    ew.ui.addButton({
      id: 'mark-here',
      label: 'Mark',
      title: 'Drop a point at the crosshairs in a "My marks" layer',
      onClick: () => {
        const layer = ew.annotations.addLayer('My marks', { color: '#ffd700' });
        layer.addPoint(ew.view.position(), 'marked ' + new Date().toLocaleTimeString());
      },
    });
  }, 1000);
})();

Need something the API does not have?

Ask in the API requests thread on the forum and we will add it. Until then, eyewire.viewer (the same as window.viewer) gives you the whole neuroglancer viewer.

Good to know

Scripts are made by players and are not reviewed by the EyeWire team.