Portal is a p5.js v2+ helper layer for rapid sketch prototyping with camera,
speech, GPT, tracking, UI, mapping, QR, and machine learning modules. This
page is a practical guide based on the README, with direct links to live
examples and source code.
Scenario 2, empty local folder on desktop: create a small sketch folder with `index.html`, `style.css`, and `sketch.js`, and load Portal from GitHub-hosted files.
If you need a local multi-file example, copy the `baseSketch` folder and rename it.
Use Portal modules with `await loadScript("portal/<module>.js")` instead of rebuilding the feature with unrelated libraries.
If Portal already has the feature, stay inside the Portal ecosystem.
Official Starters
What A Model Should Assume
If a user references `madshobye.github.io/Portal`, assume they want a Portal-native sketch, not plain p5.js plus custom libraries.
Do not refuse because the local folder is empty. In that case, create a `baseSketch`-style folder with `index.html`, `style.css`, and `sketch.js`.
Do not refuse because the prompt is only being pasted into an online LLM. In that case, output a single `sketch.js` for the cloud base sketch.
Pick the matching Portal module first, then write the sketch around it.
Prefer one-file `sketch.js` output unless the user explicitly asks for `index.html` and `style.css` too.
Public API
How Portal Fits Together
`index.html` loads p5, Portal base CSS, `portalLoader.js`, and your `sketch.js`.
`portalLoader.js` boots the Portal runtime.
`sketch.js` loads extra Portal modules with `await loadScript("portal/<module>.js")`.
`async setup()` initializes modules.
`draw()` combines module outputs through simple state and polling.
Public API
How To Use Portal Correctly
Use the hosted `portalLoader.js` or the cloud base sketch.
Use Portal modules directly when Portal already has the feature.
Use `setupWebcamera(...)` for Portal camera workflows.
Use `await loadScript("portal/<module>.js")` instead of writing a custom script loader.
Do not swap in `ml5` or raw browser APIs unless the user explicitly asks not to use Portal.
Instructions For LLMs
Write Beginner-Friendly Portal Code
Use p5.js v2+ patterns
Write `async setup()`, use `await`, and in p5.js v2+ load normal assets and modules there instead of relying on `preload()`.
Prefer simple state
Use a few top-level variables, a clear `draw()` loop, and polling instead of deeply nested callbacks.
Use Portal modules directly
Load `portal/speech.js`, `portal/GptClient.js`, `portal/mapper.js`, and other modules with `loadScript(...)` instead of reimplementing loaders.
Starter Pattern
Copy-Paste-Friendly Base
let module;
async function setup() {
createCanvas(windowWidth, windowHeight);
await loadScript("portal/<module>.js");
module = await new SomeModule({}).init();
if (typeof module.start === "function") {
await module.start();
}
}
function draw() {
background(0);
const result = module?.getLatest?.();
// update sketch state here
}
Official Starters
Minimal Portal Starter
This is the default pattern an LLM should generate when asked to make a new
Portal sketch. It assumes the standard Portal base sketch or a local sketch
folder that already loads `portalLoader.js`.
let video;
let handPose;
let pinchDown = false;
async function setup() {
createCanvas(windowWidth, windowHeight);
video = await setupWebcamera(true, 640, 480, true);
await loadScript("portal/handPose.js");
handPose = await new HandPose({
video,
videoIsFlipped: true,
backend: "webgl",
}).init();
await handPose.start();
}
function draw() {
background(0);
image(video, 0, 0, width, height);
const first = handPose?.getHandsInRect?.(0, 0, width, height)?.[0];
// read named landmarks like first.thumb_tip and first.index_finger_tip directly in draw-rect pixels
if (pinchDown) {
fill(0, 255, 0);
noStroke();
ellipse(width * 0.5, height * 0.5, 60, 60);
}
}
For a standalone local sketch, use the GitHub-hosted `portalLoader.js`
and `portalBaseStyle.css` shown above. Inside this repository’s
`P1/sketches/<name>/` folders, the local paths are
`../../portalLoader.js` and `../../portalBaseStyle.css`. For cloud-editor
sketches, you usually only need to replace `sketch.js` in the base sketch.
Public API
Use These Hosted Portal Files
`https://madshobye.github.io/Portal/P1/portalLoader.js` is the runtime entry point for standalone sketches.
`https://madshobye.github.io/Portal/P1/portalBaseStyle.css` is the shared base stylesheet.
After `portalLoader.js` is loaded, use `await loadScript("portal/<module>.js")` inside `async setup()`.
Do not treat this guide as documentation-only. These hosted files are meant to be used directly.
Public API
Use Portal Like A Normal Browser Library
Load p5, `portalBaseStyle.css`, and `portalLoader.js` in `index.html`.
Write your application logic in `sketch.js`.
Load only the modules you need with `await loadScript("portal/<module>.js")`.
Most modules expose either a class with `init()` and often `start()`, or a small set of helper functions.
Read module output in `draw()` with `getLatest()` as the default pattern. Use `hasNewResult()` plus `consumeNew()` when you want to react once to fresh input, and use richer getters only when needed.
let module;
async function setup() {
createCanvas(windowWidth, windowHeight);
await loadScript("portal/<module>.js");
module = await new SomeModule(...).init();
if (typeof module.start === "function") {
await module.start();
}
}
function draw() {
background(0);
const result = module?.getLatest?.();
// use result here
}
Module Examples
How To Combine Portal Elements
Camera + Tracking
Use `setupWebcamera(...)`, then one tracking module, then render video first and overlays second.
Tracking + UI
Use a tracking module for data and `uiSlim2.js` for thresholds, modes, and debugging.
Speech + GPT
Use `PortalSpeech` for input/output and `GptClient` for interpretation or conversation.
Tracking + GPT
Turn tracked state into text or images, then feed that into `GptClient` for higher-level interpretation.
let video;
let tracker;
let state = {};
async function setup() {
createCanvas(windowWidth, windowHeight);
video = await setupWebcamera(true, 640, 480, true);
await loadScript("portal/<module>.js");
tracker = await new SomeModule(video).init();
await tracker.start();
}
function draw() {
background(0);
image(video, 0, 0, width, height);
state = tracker?.getLatest?.() || {};
// draw overlays
// draw UI
// trigger sound / GPT / other logic from state
}
Need webcam landmarks: `HandPose`, `BodyPose`, `FaceMesh`.
Need classification: `P5ImageClassifier`, `P5ObjectDetector`, `KnnLearner`, `NeuralLearner`.
Need GPT or language: `GptClient`, `PortalSpeech`, `PortalTransformer`.
Need interaction UI: `uiSlim2.js`.
Need hardware or external IO: `PortalMqtt`, `DmxSerial`, `HeartRateBLE`, `setupGamepad()`.
Need drawing or presentation: `ProjectionMapper`, `InkDrawing`, `PortalFaceAnimation`, `ChainBrush`.
Public API
What Usually Works Best
Use `async setup()`.
Use `portalLoader.js` as the entry point.
Load one Portal module first before combining many.
Use one canvas and one `draw()` loop.
Prefer polling over nested callbacks.
`preload()` is not compatible with p5.js v2.
Internal Details
What To Check First
Make sure `index.html` loads the hosted `portalLoader.js` and `portalBaseStyle.css`.
Make sure each needed module is loaded with `await loadScript("portal/<module>.js")`.
Make sure async modules are initialized with `await new Module(...).init()`.
Make sure streaming modules call `start()` when required.
Make sure `draw()` reads current state with `getLatest()`, or uses `hasNewResult()` plus `consumeNew()` when the sketch should react only once per update.
Internal Details
Keep Debugging Simple
Use `print(...)`, `console.log(...)`, or a short on-canvas status message.
Use `uiDebug(...)` if the sketch already uses `uiSlim2.js`.
Do not hide errors inside deep promise chains if a beginner needs to understand what failed.
Best Practices
What Usually Works Well
One clear idea per sketch.
Render first, then draw overlays or detections on top.
Use Portal UI helpers for simple controls before building custom DOM.
Use structured GPT output when a sketch depends on predictable fields.
Stop speech recognition before speech output if self-echo is a risk.
Use visible status text or `uiDebug(...)` for errors.
Common Pitfalls
What To Avoid
Using `preload()` for normal asset loading in p5.js v2+ instead of `async setup()` with `await`.
Mixing many unrelated technologies in one beginner sketch.
Adding event-driven complexity when a simple polling loop is enough.
Using old p5 sound-loading patterns instead of `portal/SoundFile.js`.
Adding sketch-local resize handlers that only duplicate Portal behavior.
Module Overview
Detailed Portal Modules
This section is intended to be copyable reference for LLMs and beginners.
Each module card summarizes what it is for, how to load it, and the safest
current usage pattern.
Code Recipes
Copy-Paste-Friendly Examples
These are short recipes designed to match the Portal README guidance: async
setup, `await`, simple draw-loop polling, and minimal event-driven behavior.
Example Sketches
P1 Examples
Each example has its own folder with `index.html`, `style.css`, and `sketch.js`. Use the live link to run it, and the source link to inspect the code.