← Back to list

P5 Exhibition Mode: A Small Runtime Layer for Generative Art in Public Space

An open-source code library by Ronen Tanchum https://github.com/RonenTanchum/p5-exhibition-mode

Tanchum Ronen · 2026-05-09 11:33 · 0 claps · 16.2 min read
#javascript #generative-art #p5js #art #exhibition
Open on Medium ↗
Wiki topics: CUL · Culture & Media 🌐 · Web Development 🔓 · Open Source 🔭 · Astronomy & Space 📚 · Books & Reading 🎨 · Fine Art

P5 Exhibition Mode: A Small Runtime Layer for Generative Art in Public Space

An open-source code library by Ronen Tanchum https://github.com/RonenTanchum/p5-exhibition-mode

Have you ever needed to code on-site during an exhibition install, while the projector was already mounted, the wall label was waiting, and the browser was still behaving like a browser?

Or wanted to capture a clean video pass through ten specific hashes from a generative project, without manually changing URLs, reopening tabs, or trusting a screen-recording dialog in front of an audience?

I built p5-exhibition-mode for that exact situation.

It is a small runtime layer for p5.js artworks. Its purpose is not to replace the artwork, the sketch, or the artist’s system. It sits around the work and handles the operational conditions that become visible only when generative software leaves the studio: fullscreen behavior, browser gestures, cursor state, display rotation, overlays, QR labels, hash playback, playlisting, capture, and runtime diagnostics.

In other words, it treats a browser sketch less like a webpage and more like an installed artwork.

The Problem: Generative Art Is Often Stable Until It Is Installed

Most generative projects are developed in conditions that are forgiving. The artist has the console open. The browser can be refreshed. A small UI panel can remain visible. Screen recording can include the whole desktop. If the work fails, the artist is nearby.

Exhibition conditions are different.

A public installation needs to run for hours or days. It may be shown on a rotated display, an unfamiliar GPU, a borrowed machine, or a kiosk where nobody should touch the trackpad. A collector, curator, technician, or gallery assistant may need to restart the work without understanding the code. The artwork may need to rotate between several local HTML folders or several live URLs. The artist may need to show a specific set of hashes, or let the work generate new hashes continuously.

Many of these requirements are not conceptually difficult, but they are operationally fragile. They tend to be rebuilt ad hoc for each show.

p5-exhibition-mode tries to make that layer explicit.

What the Tool Does

The tool wraps a p5.js sketch with exhibition controls:

  • Fullscreen and kiosk behavior
  • Context-menu, touch gesture, scroll, and cursor locks
  • Device pixel ratio limits for performance
  • Rotation for portrait screens and rotated projectors
  • Runtime diagnostics for FPS, resolution, memory, uptime, watchdogs, and logs
  • Floating title, free text, hash, and QR overlays
  • Automatic stacking when title, text, and QR share the same screen position
  • Per-playlist title, artist, year, and free-text labels
  • A playlist system for local paths and remote artwork URLs
  • A separate hash playlist for curated hashes
  • Random hash generation for exploration
  • Custom artwork URL parameters such as ui=false
  • Direct canvas capture for stills and video
  • Browser H.264 MP4 where supported, with WebM fallback
  • Local FFmpeg helper commands for H.264 or ProRes delivery conversion
  • Save/load JSON configuration for repeatable installation setup

The interface is intentionally practical. It is not a portfolio page and not a CMS. It is a control surface for an artwork while the artwork is running.

A Runtime Panel for the Exhibition Situation

The runtime panel opens with Shift + G. It is divided into tabs:

  • Runtime: status, input locks, rotation, custom URL parameters, and hash recording
  • Overlay: global labels, per-playlist item labels, QR, hash overlay, typography, safe areas, and card mode
  • Playlist: artwork URLs/local paths, specific hashes, random hash mode, and intervals
  • Capture: recording source, codec, bitrate, FPS, and file naming
  • Log: runtime events and warnings

Suggested screenshots for publication:

  • docs/assets/panel-runtime-url-params.png — runtime controls and URL parameters
  • docs/assets/panel-overlay.png — global overlay typography, QR, and layout controls
  • docs/assets/panel-overlay-playlist-metadata.png — compact per-playlist item label editor
  • docs/assets/panel-playlist.png — artwork rows and hash playlist
  • docs/assets/panel-capture.png — direct canvas recording controls

This matters because the person operating the installation should not have to edit code to adjust a QR code, hide an artwork’s internal UI, rotate the display, or move through a list of hashes.

It also matters when one screen rotates through several works. A playlist is rarely only a list of URLs. Each work may need its own title, year, artist credit, short description, and project link. The Overlay tab therefore has two modes: a global fallback label for the whole runtime, and per-playlist item labels for the specific artwork currently playing. Empty item fields fall back to the global label, so the operator can keep the setup concise when several works share the same artist or context.

For example, a generative project might expose its own interface by default. In a gallery, the work should load as:

[https://art.example.com/eternal-tides/index.html?ui=false&hash=0x](https://art.example.com/eternal-tides/index.html?ui=false&hash=0x)...

In p5-exhibition-mode, ui=false becomes a reusable artwork URL parameter. The generated hash is appended after it. The artist can set this in code, from the Runtime tab, from JSON, or from a launch URL.

Hashes as Exhibition Material

Generative art often treats the hash as both seed and edition index. But practical exhibition workflows around hashes are still awkward.

Sometimes you want random hashes. Sometimes you want a specific list. Sometimes you want to rotate the artwork every ten minutes but change the hash every forty-five seconds. Sometimes you want to record ten selected states for documentation.

The tool separates these concerns:

  • Artwork playlist: which page or local path is being shown
  • Hash playlist: which exact hash values should be used
  • Random hash mode: whether the runtime should generate new hashes instead

When random hash mode is enabled, it overrides the saved specific hash list. The specific list is kept, but collapsed and inactive. When random mode is turned off, the saved curated hashes become active again.

That distinction is important. It prevents a common ambiguity: “I turned random hashes on, but why is the same saved hash still appearing?”

Capture Without Screen Recording

The browser is not a neutral exhibition environment. Standard screen capture asks for permissions, includes browser chrome if misconfigured, and can behave differently across operating systems.

This tool records directly from the artwork canvas and the overlay layout at the current browser window size. The user can resize the browser to choose the output aspect ratio. There is no need to enter fullscreen just to record. There is no screen-share popup.

The capture system is designed around browser reality:

  • H.264 MP4 is the preferred user-facing target when the browser supports it.
  • WebM remains the safest browser fallback.
  • ProRes is not a browser MediaRecorder format, so the included helper converts after recording using local FFmpeg.

This is less romantic than pretending the browser can encode every professional format natively, but it is more useful.

Why Not Just Build This Into Each Artwork?

You can. Many artists do.

But rebuilding the same exhibition shell for every project creates maintenance debt. It also mixes the artwork’s internal system with the conditions of display. A sketch should be able to focus on composition, behavior, and form. The exhibition runtime can handle the operational layer.

That separation also helps with institutional work. A JSON configuration can be saved for a specific venue. A technical rider can specify rotation, playlist paths, capture settings, watchdog behavior, and URL params. The artwork itself remains the artwork.

A Tool for Artists, Technicians, and Institutions

The intended user is not only the artist in the studio. It is also the person at the venue with a keyboard, a display, and a problem.

The tool is useful when:

  • a work needs to run as a kiosk
  • a local playlist of HTML folders needs to rotate
  • a live project needs its UI hidden by URL parameter
  • each artwork in a playlist needs its own wall-label text
  • a curated hash list needs to be presented
  • random hashes need to be explored or recorded
  • a title/QR label needs to sit on top of the artwork
  • a portrait display requires rotation
  • a clean MP4 or WebM recording is needed
  • the installation needs a repeatable saved configuration

It is also useful during development because it makes exhibition assumptions visible early.

There is also an llms.txt file for AI agents. The point is not to make the artwork autonomous. It is to make the installation layer legible: what files matter, how to configure a show, what defaults are appropriate, how to use playlist metadata, and what to test before opening. If an assistant is helping prepare a screen-based exhibition, it can read the repository like an installation manual rather than guessing from the source code.

A Small Layer, Not a Framework

The project is deliberately narrow. It is an ES module with no runtime dependencies. It does not impose a rendering model. It does not replace p5.js. It does not require a backend unless local file serving is needed for browser security reasons.

That constraint is part of the design. Exhibition tools should be understandable under pressure. If something fails on-site, the artist should be able to reason about it.

The included local helper server exists for one reason: browsers cannot safely read arbitrary local folders from a webpage. The helper serves allowed local roots so playlist entries can use stable local paths. This makes local mirrored artwork sites practical for capture and exhibition without relying on remote network availability.

Closing

p5-exhibition-mode came out of a practical need: making generative browser artworks easier to install, inspect, rotate, seed, label, and capture.

It is a modest tool, but the problem it addresses is real. Generative art does not only need code that produces images. It also needs code that survives display conditions.

The exhibition runtime is where those two realities meet.

Technical

p5 Exhibition Mode

p5-exhibition-mode is a small runtime layer for showing p5.js and browser-based generative artworks in real exhibition conditions.

It is meant for the moments when the artwork already exists, but the room is not behaving like the studio: the browser wants to scroll, the display is rotated, fullscreen drops, the curator needs one specific hash, the installer needs to hide an artwork UI with ui=false, or someone asks for a clean MP4 capture five minutes before opening.

The library adds those practical controls around the artwork without changing the artwork itself.

What It Solves

Generative artworks are usually built for a browser, but exhibitions ask for a different kind of reliability:

  • keep the work fullscreen or kiosk-ready
  • suppress right-clicks, scroll bounce, pinch zoom, and browser gestures
  • hide the cursor during display
  • lock pixel ratio so the machine does not overload itself
  • rotate the artwork for portrait screens or rotated projectors
  • add a discreet title, description, QR code, or hash overlay
  • rotate through artwork URLs, local files, and selected hashes
  • generate new hashes at intervals for live exploration
  • add artwork-specific URL params such as ui=false
  • record the artwork canvas directly without macOS screen-capture popups
  • save and reload a complete runtime setup as JSON

The runtime panel is hidden until you need it. Press Shift + G to open it.

Examples

The example screenshots below use Apex and are captured after a 10-second build delay, so the artwork has time to develop before the frame is taken.

Apex Runtime

Editorial Title

Bottom Caption

Card Label With QR

Right-Aligned Label

Minimal Hash Label

Rotated Display

Interface

The screenshots below are cropped to the control surface. They show the UI itself rather than a full-screen artwork composition.

For a practical show-prep workflow, see docs/exhibition-setup-guide.md. AI agents can also read llms.txt for a concise setup map.

Runtime

The Runtime tab is for installation settings: fullscreen, kiosk locks, cursor behavior, display rotation, watchdog settings, diagnostics, hash recording, and artwork URL params.

Use this tab when the screen or browser needs to behave like exhibition hardware. URL params are useful when a work needs display flags that are not the seed hash, for example ui=false or quality=gallery. These params are applied to every loaded artwork before the hash.

Overlay

The Overlay tab controls the exhibition label layer. You can show a title, artist, year, free text, QR code, and a small hash overlay.

The first subtab edits Global Text, which is the fallback label for the whole runtime. The second subtab, Per Playlist Item, lets each playlist URL or local path carry its own title, artist, year, and short wall text. Empty per-item fields fall back to the global fields.

The per-item editor stays compact by default: choose a playlist item, review its effective label, move to the previous/next item, or add a new row. Open Edit Item Text only when you need to change that item’s title, artist, year, or wall text.

Floating overlays automatically stack when they share the same position, so title, text, and QR do not collide. Card mode groups title, text, artist/year, and QR into one compact wall-label block.

Playlist

The Playlist tab separates artwork URLs/local paths from hash playback. Artwork order can loop or randomize. Hash order can also loop or randomize.

Use playlist rows for the pages being shown. Use the hash playlist for the generated states being shown. If random hashes are enabled, the specific-hash section collapses and the runtime generates fresh 0x... hashes instead. Your curated hash list stays saved and becomes active again when random mode is turned off.

Install

npm install p5-exhibition-mode

For local development inside this repository:

import { createExhibitionMode } from "./src/index.js";

For an installed package:

import { createExhibitionMode } from "p5-exhibition-mode";

Basic Usage

import { createExhibitionMode } from "p5-exhibition-mode";
const exhibition = createExhibitionMode({
  title: "Spring",
  artist: "Phenomena Labs",
  year: "2026",
  fullscreen: true,
  kiosk: true,
  maxPixelRatio: 2,
  showTitleOverlay: true,
  titleOverlayPosition: "top-left",
  titleOverlayFont: "editorial",
  titleOverlayColor: "white"
});
function setup() {
  exhibition.setup();
  exhibition.applyPixelRatio();
  createCanvas(windowWidth, windowHeight);
}
function draw() {
  exhibition.tick();
  background(0);
}

Press Shift + G to open the panel.

Local Exhibition Helper

Browsers do not let a webpage read arbitrary files from your computer. Selecting one index.html also does not give the browser access to neighboring JavaScript, image, shader, or data files. That is why complex local artworks often show a black screen when loaded from a raw file picker.

For a kiosk or exhibition machine, run the helper server from the folder that contains your artworks:

npx p5-exhibition-helper --root /Users/you/Artworks --port 4177

Then open:

http://127.0.0.1:4177/

Playlist entries can now use served local paths:

./ClassicalRevival/index.html
./Apex/index.html
/Users/you/Artworks/Rococo/index.html

If your live site is mirrored locally, point urlMirrorRoot at the local mirror and keep live URLs in the playlist:

createExhibitionMode({
  localFiles: {
    urlMirrorRoot: "/Users/you/Projects/art.phenomenalabs.com"
  },
  playlist: {
    enabled: true,
    items: [
      "https://art.phenomenalabs.com/ClassicalRevival/index.html",
      "https://art.phenomenalabs.com/Rococo/index.html"
    ]
  }
});

With that setup, the helper serves the local copy as same-origin content. That matters for direct canvas capture, because browsers block pixel access to cross-origin iframes.

Playlist And Hashes

Playlist mode can rotate artwork pages and hashes independently.

const exhibition = createExhibitionMode({
  playlist: {
    enabled: true,
    itemOrder: "loop",
    intervalValue: 3,
    intervalUnit: "minutes",
    items: [
      {
        url: "./works/apex/index.html",
        title: "Apex",
        artist: "Phenomena Labs",
        year: "2026",
        freeText: "Live generative landscape for the main display."
      },
      {
        url: "https://art.phenomenalabs.com/ClassicalRevival/index.html",
        title: "Classical Revival",
        artist: "Ronen Tanchum",
        year: "2026"
      }
    ],
    randomHash: false,
    hashOrder: "loop",
    hashIntervalValue: 30,
    hashIntervalUnit: "seconds",
    hashes: [
      "0x1111111111111111111111111111111111111111111111111111111111111111",
      "0x2222222222222222222222222222222222222222222222222222222222222222"
    ],
    hashParam: "hash"
  }
});

Playlist items may be plain URL strings or objects. Object items can carry their own overlay text:

{
  url: "./works/spring/index.html",
  title: "Spring",
  artist: "Phenomena Labs",
  year: "2026",
  freeText: "Short wall-label text for this specific playlist entry."
}

When a playlist item has title, artist, year, or freeText, those values override the global Overlay tab text only while that item is playing. Empty item fields fall back to the global overlay fields. In the panel, open Overlay → Per Playlist Item to edit this text per playlist URL/local path.

For continuous random exploration:

createExhibitionMode({
  playlist: {
    enabled: true,
    randomHash: true,
    hashIntervalValue: 20,
    hashIntervalUnit: "seconds",
    items: ["https://art.example.com/field-system/index.html"]
  }
});

Recommended artwork behavior:

  • read ?hash= once during startup
  • convert the hash string into a deterministic numeric seed
  • use that seed for randomSeed, noiseSeed, or your own PRNG
  • keep display flags such as ?ui=false separate from the hash
  • reload or restart the sketch when a new hash should create a new composition

Minimal p5 example:

function hashToSeed(value) {
  let seed = 0;
  for (let i = 0; i < value.length; i += 1) {
    seed = (seed * 31 + value.charCodeAt(i)) >>> 0;
  }
  return seed;
}
function setup() {
  const params = new URLSearchParams(window.location.search);
  const hash = params.get("hash") || "default";
  const seed = hashToSeed(hash);
  randomSeed(seed);
  noiseSeed(seed);
  createCanvas(windowWidth, windowHeight);
}

If your artwork expects another parameter name, change hashParam:

playlist: {
  randomHash: true,
  hashParam: "seed",
  items: ["https://example.com/work/?ui=false"]
}

Artwork URL Params

Some artworks need custom display params. For example, Eternal Tides might need ui=false to hide its own interface in an exhibition.

Use customUrlParams in code, or add rows in the Runtime tab:

createExhibitionMode({
  customUrlParams: [
    { name: "ui", value: "false" },
    { name: "quality", value: "gallery" }
  ],
  playlist: {
    enabled: true,
    randomHash: true,
    items: ["https://art.example.com/eternal-tides/index.html"]
  }
});

Generated URL:

https://art.example.com/eternal-tides/index.html?ui=false&quality=gallery&hash=0x1fa5259da261374fd4c92eb2df1fd6b4b1c01f5b196fa12f275dff183e4858ad

Custom params are applied before the hash. If the artwork URL already contains the same param name, the runtime value replaces it.

Capture

The Capture tab records the artwork canvas and overlay layout directly at the current browser window size. It does not request screen capture, include browser chrome, or force fullscreen. Resize the browser to choose the output framing.

The default target is H.264 MP4 when the browser supports it. If the browser rejects H.264, the runtime falls back to the browser’s available recorder format and reports the actual output type. WebM is still the safest long-recording fallback.

For ProRes or post-processing delivery, record first and convert locally:

p5-exhibition-capture --input exhibition-capture.webm --preset prores
p5-exhibition-capture --input exhibition-capture.webm --preset h264 --output exhibition-capture.mp4

ProRes is not available from browser-only MediaRecorder. The helper uses FFmpeg on the machine. On macOS:

brew install ffmpeg

Official FFmpeg downloads are listed at https://www.ffmpeg.org/download.html. The GitHub mirror is https://github.com/FFmpeg/FFmpeg.

Runtime Panel

The panel includes:

  • Runtime: fullscreen, kiosk locks, cursor, rotation, watchdog, diagnostics, and artwork URL params
  • Overlay: global title text, per-playlist item text, QR, hash overlay, safe border, font, and card/floating layout
  • Playlist: artwork URLs/local paths, specific hashes, random hashes, loop/random order, and intervals
  • Capture: direct canvas stills and video recording
  • Log: runtime messages, warnings, browser errors, playlist events, and capture status

Use Save JSON to download the full runtime configuration. Use Load JSON to restore it. Use Save Defaults to store the current setup as the browser’s restore point for that machine.

Panel settings are persisted to localStorage by default using storageKey: "p5-exhibition-mode-config".

Code-Only Control

Every panel control is also available through the API, so a production build can hide the panel and drive the runtime from a custom control surface, socket bridge, venue config, or remote admin page.

const exhibition = createExhibitionMode({
  panel: false,
  fullscreen: true,
  kiosk: true,
  rotation: 90,
  persist: true,
  storageKey: "gallery-a-main-wall",
  playlist: {
    enabled: true,
    intervalValue: 12,
    intervalUnit: "minutes",
    randomHash: true,
    hashIntervalValue: 45,
    hashIntervalUnit: "seconds",
    items: ["./works/classical-revival/index.html"]
  }
});
exhibition
  .setup()
  .setInputLocks({ contextMenu: true, touchGestures: true, scroll: true })
  .setCursor({ hide: true, mode: "idle", idleMs: 3000 })
  .setWatchdog({ enabled: true, minFps: 12, seconds: 30, reload: true });
exhibition.setArtworkMetadata({
  title: "Classical Revival",
  artist: "Ronen Tanchum",
  year: "2026",
  showTitleOverlay: true,
  freeText: "Live generative study\nCustom edition for the south wall.",
  showFreeText: true,
  showHashOverlay: true,
  hashOverlayColor: "white",
  overlayLayout: "card"
});
exhibition.setCustomUrlParams([{ name: "ui", value: "false" }]);
exhibition.saveConfig();

Useful methods:

  • setFullscreen(value), enterFullscreen(), exitFullscreen()
  • setKiosk(value)
  • setInputLocks({ contextMenu, touchGestures, scroll })
  • setCursor({ hide, mode, idleMs })
  • setRotation(degrees)
  • setAccessibility({ reducedMotion, highContrast })
  • setArtworkMetadata(options)
  • setQrOptions(options)
  • setOverlayLayout("separate" | "card")
  • setWatchdog(options), setHealthCheck(options)
  • setPlaylistOptions(options), setPlaylistItems(items), setPlaylistHashes(hashes)
  • setPlaylistItemMetadata(index, { title, artist, year, freeText })
  • setCustomUrlParams(params)
  • setPlaylistIntervalParts(value, unit), setPlaylistHashIntervalParts(value, unit)
  • nextPlaylistItem(), previousPlaylistItem(), previewPlaylistUrl(url)
  • getConfig(), loadConfig(config), saveConfig(), exportConfig()

URL Commands

Runtime settings can be controlled from the page URL. Commands can be written directly, such as ?ui=false, or namespaced, such as ?p5em.ui=false.

Example kiosk launch:

http://127.0.0.1:4177/demo/?ui=false&fullscreen=true&kiosk=true&rotation=90&showTitle=true&title=Spring&artist=Phenomena%20Labs&year=2026&layout=card

Example playlist launch:

http://127.0.0.1:4177/demo/?playlistEnabled=true&urls=https%3A%2F%2Fart.phenomenalabs.com%2FClassicalRevival%2Findex.html%7Chttps%3A%2F%2Fart.phenomenalabs.com%2FRococo%2Findex.html&playlistInterval=40&playlistUnit=seconds&randomHash=true&hashInterval=5&hashUnit=seconds

Example with artwork display params:

http://127.0.0.1:4177/demo/?playlistEnabled=true&urls=https%3A%2F%2Fart.example.com%2Feternal-tides%2Findex.html&artworkParams=ui%3Dfalse%7Cquality%3Dgallery&randomHash=true

Common URL commands:

CommandValuesMeaningui / paneltrue, falseShow the runtime panelfullscreentrue, falseRequest fullscreen after user gesturekiosktrue, falseEnable kiosk behaviorcontexttrue, falseDisable context menutouchtrue, falseDisable browser touch gesturesscrolltrue, falsePrevent page scrollingcursortrue, falseHide cursorcursorModealways, idleCursor hide behaviordprnumberMax device pixel ratiorotation0, 90, 180, 270Rotate artwork and overlaystitle, artist, yeartextOverlay metadatashowTitletrue, falseShow title overlaytitleFontfont keyOverlay font familytitleColorwhite, gray, blackOverlay title colortitlePositionpositionTitle/card positiontext / freeTexttextFree text overlay contentshowTexttrue, falseShow free textshowHash / showHashOverlaytrue, falseShow small hash overlayhashPositionbottom-left, bottom-rightHash overlay positionhashColorwhite, blackHash overlay colorlayoutseparate, cardFloating overlays or card labelqrURLQR linkshowQrtrue, falseShow QR codeartworkParams / customParams``name=valuename2=value2``playlistEnabledtrue, falseEnable playlist modeurls / playlist``url1url2``playlistOrderloop, randomArtwork orderplaylistIntervalnumberArtwork interval valueplaylistUnitseconds, minutes, hoursArtwork interval unithashes``hash1hash2``hashOrderloop, randomSpecific hash orderrandomHashtrue, falseGenerate random hasheshashIntervalnumberHash interval valuehashUnitseconds, minutes, hoursHash interval unithashParamtextHash parameter name, default hash

For multiple playlist URLs, hashes, or artwork params, separate entries with | and URL-encode the full value.

Options Reference

createExhibitionMode({
  title: "Untitled Artwork",
  artist: "",
  year: "",
  showTitleOverlay: false,
  titleOverlayFont: "mono",
  titleOverlayColor: "white",
  titleOverlayPosition: "top-left",
  titleOverlaySize: 11,
  titleOverlayBold: false,
  titleOverlayItalic: false,
  freeText: "",
  showFreeText: false,
  freeTextPosition: "bottom-left",
  freeTextSize: 10,
  showHashOverlay: false,
  hashOverlayPosition: "bottom-left",
  hashOverlaySafeArea: 18,
  hashOverlaySize: 9,
  hashOverlayColor: "white",
  overlayLayout: "separate",
  cardQrPlacement: "below",
  overlaySafeArea: 18,
  qrLink: "",
  showQr: false,
  qrPosition: "bottom-right",
  qrSize: 96,
  seed: null,
  fullscreen: true,
  kiosk: true,
  disableContextMenu: true,
  disableTouchGestures: true,
  preventScroll: true,
  hideCursor: false,
  hideCursorMode: "always",
  cursorIdleMs: 2400,
  idleReset: false,
  maxPixelRatio: 2,
  rotation: 0,
  refreshOnRotation: true,
  accessibility: {
    reducedMotion: false,
    highContrast: false
  },
  watchdog: {
    enabled: false,
    minFps: 12,
    seconds: 30,
    reload: true
  },
  logging: {
    enabled: true,
    maxEntries: 300
  },
  healthCheck: {
    enabled: false,
    url: "",
    intervalSeconds: 60
  },
  capture: {
    filename: "exhibition-capture",
    source: "auto",
    codec: "h264",
    videoBitsPerSecond: 30000000,
    frameRate: 60,
    includeAudio: false,
    hidePanelDuringCapture: false
  },
  localFiles: {
    endpoint: "/__p5em/files",
    absolutePrefix: "/__p5em/abs/",
    urlMirrorRoot: "",
    fallbackFilePreview: true
  },
  playlist: {
    enabled: false,
    // Each item can be a URL string or an object with per-item overlay text:
    // { url, title, artist, year, freeText, randomHash, hashParam }
    items: [],
    itemOrder: "loop",
    intervalValue: 2,
    intervalUnit: "minutes",
    intervalSeconds: 120,
    hashes: [],
    hashOrder: "loop",
    hashIntervalValue: 2,
    hashIntervalUnit: "minutes",
    hashIntervalSeconds: 120,
    randomHash: false,
    hashParam: "hash",
    startIndex: 0,
    startHashIndex: 0
  },
  customUrlParams: [],
  monitor: true,
  panel: true,
  panelKey: "g",
  ui: {
    activeTab: "runtime",
    panelBounds: null
  },
  urlParams: true,
  onReset: undefined,
  onScreenshot: undefined,
  onDiagnostics: undefined
});

Sensor Bridge

The sensor bridge exposes real-world values as simple numeric inputs for p5.js, Three.js, GLSL, or your own renderer.

import { createSensorBridge } from "p5-exhibition-mode/sensors";
const sensors = createSensorBridge({
  type: "websocket",
  url: "ws://localhost:8080",
  map: (data) => ({
    presence: data.people ?? 0,
    temperature: data.temp ?? 0
  })
}).start();
function draw() {
  const presence = sensors.get("presence");
}

Supported first-pass inputs:

  • manual values
  • JSON polling
  • WebSocket streams

Included Project Files

The repository also includes:

  • templates/technical-rider.md
  • schemas/artwork-manifest.schema.json
  • examples/artwork-manifest.example.json
  • examples/runtime-config.example.json
  • docs/articles/medium-draft-p5-exhibition-mode.md

Demo

npm run demo

Open:

http://127.0.0.1:4177/demo/

Notes

Browsers require a user gesture before entering fullscreen. Exhibition Mode listens for the first pointer or keyboard event and requests fullscreen from that gesture.

Memory reporting depends on browser support and may show as unavailable.

Open Source, Credit, And Support

Developed @ Phenomena Labs.

This project is open source under the MIT license. You can use it, adapt it, and include it in exhibition projects. Credit is appreciated when the tool is used publicly, in documentation, or in production installs.

Contributions help keep the tool maintained and available for artists. PayPal contributions can be sent to info@phenomenalabs.com; crypto contributions can be sent to phenomenalabs.eth.


메타데이터
post_id
6ae87ae3c6ea
slug
p5-exhibition-mode-a-small-runtime-layer-for-generative-art-in-public-space-6ae87ae3c6ea
url
https://medium.com/@tanchum.ronen/p5-exhibition-mode-a-small-runtime-layer-for-generative-art-in-public-space-6ae87ae3c6ea
canonical_url
https://medium.com/@tanchum.ronen/p5-exhibition-mode-a-small-runtime-layer-for-generative-art-in-public-space-6ae87ae3c6ea
author_url
https://medium.com/@tanchum.ronen
status
ok
fetched_at
2026-06-20 20:29:01