Better editor support for p5.js sketchspaces in Neovim.
- Features β¨
- Requirements π
- Installation πΎ
- What's a sketchspace ?
- Quick Start π
- Commands π
- Configuration β‘
- Auto Commands π
- Keyboard Shortcuts β¨οΈ
- Troubleshooting π§
- License π
- Live Server π - Auto-reload preview in browser
- Package Management π¦ - Install contributor libraries
- Sketchspace π - Minimal project setup
- GitHub Gist π - Share sketches (synced to sketchspace)
- CDP DevTools π οΈ - Full Chrome DevTools Protocol panel: Console, Network, Eval, Debugger, Performance, Info tabs
- Console πΊ - View browser logs in Neovim (deprecated β use CDP panel)
- p5.js Docs π - Built-in reference via snacks.picker
- Snippets βοΈ - 100+ p5.js code snippets (VS Code JSON format, compatible with luasnip, blink.cmp, nvim-cmp)
nvim>= 0.11.0- Python 3.7+ (for development server)
- websockets Python package (
python3-websocketson Debian/Ubuntu, orsudo pipx install websockets) curl(for console streaming)lsof(checking ports)
-- lazy.nvim (installs latest release)
{
"prjctimg/p5.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
},
config = function()
require("p5").setup({})
end
}A directory that has a p5.json file is called a sketchspace. The file looks like this:
{
"version": "2.0.0",
"major": 2,
"libs": {},
"includes": ["sketch.js"],
"gist": {
"url": "username/gistId",
"title": "My Sketch",
"description": "Description from gist comment"
}
}version: p5.js version to usemajor: p5.js major version (1or2)libs: Object with library names as keys and their versions as valuesincludes: Files to include in the Gist (default:["sketch.js"])gist: Gist metadata object (optional) withurl("user/gistId"),title, anddescription
Important
This file is needed for setting up and running sketchspaces.
It currently only works with this plugin.
:P5 create my-sketch
:P5 server
:P5 cdpOpen an interactive menu with all available commands. This is the default when
:P5 is called without arguments.
:P5 # Open interactive menuThe menu shows context-aware labels (e.g. "Stop server" when the server is running vs "Start server" when it's not).
Create a new sketchspace.
:P5 create my-sketch
:P5 createWhen prompted for a version, selecting Latest (2.x) will skip TypeScript type definitions
until the p5.js 2.x type declarations are officially released. The project is created
immediately with everything else β you can start coding right away. Run :P5 setup later
to copy types once they become available.
Setup assets in current sketchspace.
Downloads files from gist (if configured), creates default sketch.js if missing, copies assets, generates libs.js, and installs configured libraries.
:P5 setupInstall contributor libraries. Use picker or specify directly.
:P5 install ml5
:P5 install ml5 rita p5play
:P5 installRemove installed libraries.
:P5 uninstall ml5
:P5 uninstallStart/stop the development server (toggle). Opens browser automatically and enables live reload.
:P5 server
:P5 server 8080
β οΈ Deprecated β use:P5 cdpinstead. The CDP panel's Console tab provides richer output with stack traces, object previews, and structured log data.
Toggle browser console to view console.log, errors, and warnings in Neovim.
:P5 console # Opens CDP panel on Console tab (with deprecation notice)
:P5 cdp # Open CDP panel (6 tabs: Console, Network, Eval, Debug, Perf, Info)Open the Chrome DevTools Protocol panel β a full-featured debugging dashboard for your p5.js sketch with 6 tabs:
| Tab | Key | What it shows |
|---|---|---|
| Console | 1 |
console.log/warn/error/info with stack traces, timestamps, and level filtering |
| Network | 2 |
HTTP requests: method, status, duration, URL with color-coded status codes |
| Eval | 3 |
Evaluate arbitrary JS expressions in the browser context |
| Debug | 4 |
Set breakpoints, step through code, inspect call stack |
| Perf | 5 |
Live FPS (instant/1s/10s avg), JS heap, DOM nodes, FPS sparkline |
| Info | 6 |
Project info, canvas state, LSP document symbols for current buffer |
:P5 cdp # Toggle CDP panel
:P5 cdp connect # Connect to Chrome debugger
:P5 cdp disconnect # Disconnect
:P5 cdp status # Show connection status
:P5 cdp eval <expr> # Evaluate JS expression
:P5 cdp break <loc> # Set breakpoint (e.g., sketch.js:12)
:P5 cdp continue # Resume execution
:P5 cdp step # Step over
:P5 cdp stepIn # Step into
:P5 cdp stepOut # Step out
:P5 cdp perf # Open Performance tabCDP panel keymaps (context-aware per tab):
| Key | Console | Network | Eval | Debug | Perf | Info |
|---|---|---|---|---|---|---|
1-6 |
Switch tab | Switch tab | Switch tab | Switch tab | Switch tab | Switch tab |
q/<Esc> |
Close | Close | Close | Close | Close | Close |
c |
Clear | Clear | Clear | Clear | Clear | β |
r |
Refresh | Refresh | β | β | Toggle rec | Refresh LSP |
<CR> |
β | β | Enter expr | Set BP | β | Jump to sym |
f |
Filter | β | β | β | β | β |
/ |
Search | Search | β | β | β | β |
s |
β | β | β | Step over | β | β |
i |
β | β | β | Step into | β | β |
o |
β | β | β | Step out | β | β |
x |
β | β | β | Continue | β | β |
b |
β | β | β | Breakpoint | β | β |
D |
β | Clear all | β | β | β | β |
K/J |
β | β | β | β | β | Nav symbols |
g/G |
Top/Bot | Top/Bot | Top/Bot | Top/Bot | Top/Bot | Top/Bot |
Keyboard shortcuts example:
vim.keymap.set("n", "<leader>pd", ":P5 cdp<CR>", { desc = "Toggle CDP DevTools" })Browser flags: When CDP is enabled, Chrome launches with graphics-debugging flags:
--enable-gpu-rasterization, --disable-frame-rate-limit, --enable-precise-memory-info,
and more. Configure via setup({ cdp = { browser_flags = { ... } } }).
Create, sync, or edit a GitHub Gist from your sketchspace.
:P5 gist "My awesome sketch" # Create gist with title
:P5 gist # Prompt for title then create
:P5 gist sync # Bidirectional sync (title, description, files)
:P5 gist edit # Edit gist title or first commentOn creation, stores gist as an object in p5.json with url, title,
and description (from the gist's first comment).
When syncing, compares remote vs local state and prompts per difference: title, description (first comment), and each source file. Files existing on only one side are auto-accepted.
Use :P5 gist edit to change the title or first comment β changes sync
both to the remote gist and the local p5.json.
Update all installed libraries or specific ones.
:P5 update # Update all installed addons
:P5 update ml5 p5play # Update specific librariesClone all gists from a GitHub user into a local skchbk/ directory. Each gist is saved in its own subdirectory named after the gist description.
Requires a dedicated GitHub account whose gists are all valid p5.js sketches.
:P5 skchbk # Clone all gists from configured user
:P5 skchbk list # Browse local sketches or browse/list remotelyIf skchbk/ doesn't exist or is empty, :P5 skchbk list prompts you to clone all gists or browse them remotely and clone individual ones.
The skchbk/ directory is automatically excluded from gist uploads.
Open a picker of recently used sketchspaces and change directory to the selected one.
:P5 listOpen p5.js documentation via snacks.picker.
:P5 docsp5.nvim ships with 100+ p5.js code snippets in VS Code JSON format. All snippets use a p5- prefix to avoid conflicts with other snippet packs.
Snippets are auto-discovered from runtimepath. Just add a snippet loader:
LuaSnip:
require("luasnip.loaders.from_vscode").lazy_load()blink.cmp (default preset): Auto-discovered, zero configuration.
blink.cmp (luasnip preset):
-- Already using luasnip? snippets are auto-discovered via luasnip
sources = {
default = { "lsp", "path", "snippets", "buffer" },
}nvim-cmp:
cmp.setup({
snippet = {
expand = function(args)
require("luasnip").lsp_expand(args.body)
end,
},
sources = {
{ name = "luasnip" },
},
})Type p5- followed by the snippet name in a .js file to trigger:
| Category | Example Trigger | Expands To |
|---|---|---|
| Lifecycle | p5-sketch, p5-setup, p5-draw, p5-preload |
Full sketch skeleton or function |
| Paired constructs | p5-push, p5-beginshape, p5-loadpixels, p5-creategraphics |
Block with open/close calls |
| Vertex calls | p5-vertex, p5-curvevertex, p5-beziervertex |
Single vertex() inside shape |
| Basic shapes | p5-canvas, p5-circle, p5-rect, p5-line |
Common 2D primitives |
| Color & style | p5-fill, p5-stroke, p5-nofill, p5-colormode-hsb |
Color and attribute setters |
| Transformations | p5-translate, p5-rotate, p5-scale |
Coordinate transforms |
| Math & vector | p5-map, p5-constrain, p5-random, p5-vector |
Math utilities |
| Typography | p5-text, p5-textsize, p5-loadfont, p5-textalign |
Text rendering |
| Images | p5-loadimage, p5-pixels, p5-tint, p5-filter |
Image loading and processing |
| Events | p5-mousepressed, p5-keypressed, p5-mousedragged |
Input event handlers |
| 3D / WebGL | p5-box, p5-sphere, p5-orbitcontrol, p5-texture |
3D primitives and lights |
| DOM | p5-createslider, p5-createbutton, p5-createselect |
UI elements |
| Sound | p5-loadsound, p5-fft, p5-oscillator |
Audio playback and analysis |
| IO | p5-loadjson, p5-loadstrings, p5-savecanvas |
Data loading and saving |
| Common idioms | p5-forgrid, p5-forpolar, p5-particles, p5-bounce |
Multi-line patterns |
Typing p5-sketch and expanding gives:
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
}Typing p5-beginshape and expanding gives:
beginShape();
vertex(x, y)
endShape(CLOSE);All tab stops ($1, $2, etc.) are navigable; press <Tab> to jump between
them (requires luasnip or blink.cmp luasnip preset).
require("p5").setup({
-- Server settings
server = {
port = 8000, -- Server port
auto_start = false, -- Auto start server when opening sketch.js
auto_open_browser = true, -- Open browser automatically
-- Live reload settings
live_reload = {
enabled = true, -- Enable live reload
port = 12002, -- Live reload port
debounce_ms = 300, -- Debounce delay
watch_extensions = {".js", ".css", ".html", ".json"}, -- Files to watch
exclude_dirs = {".git", "node_modules", "dist", "build"} -- Exclude directories
}
},
-- Console settings
console = {
enabled = true, -- Enable console integration
position = "below", -- Window position: below, above, left, right
height = 10, -- Window height (lines)
},
-- Library settings
libraries = {
auto_update = false -- Auto update libraries on setup
},
-- Sketchbook settings
sketchbook = {
user = "" -- GitHub username whose gists form the sketchbook
},
-- CDP DevTools settings
cdp = {
enabled = false, -- Enable CDP (auto-enabled when panel opens)
remote_debugging_port = 9222, -- Chrome remote debugging port
browser_flags = { -- Extra Chrome flags for graphics debugging
"--no-first-run",
"--no-default-browser-check",
"--enable-gpu-rasterization",
"--disable-frame-rate-limit",
"--disable-gpu-driver-bug-workarounds",
"--enable-precise-memory-info",
"--disable-software-rasterizer",
},
},
})Auto-start server when opening a sketch.js file:
-- Auto-start server when opening sketch.js
vim.api.nvim_create_autocmd({ "BufEnter" }, {
pattern = "sketch.js",
callback = function()
vim.cmd("P5 server")
end
})
-- Auto-open CDP panel when server starts
vim.api.nvim_create_autocmd({ "User", "P5ServerStarted" }, {
callback = function()
vim.cmd("P5 cdp")
end
})Example keybindings using <leader>p prefix:
-- General
vim.keymap.set("n", "<leader>p5", ":P5<CR>", { desc = "Open p5.nvim picker" })
-- Project
vim.keymap.set("n", "<leader>pc", ":P5 create ", { desc = "Create project" })
vim.keymap.set("n", "<leader>ps", ":P5 setup<CR>", { desc = "Setup project" })
-- Server
vim.keymap.set("n", "<leader>pss", ":P5 server<CR>", { desc = "Toggle server" })
vim.keymap.set("n", "<leader>pso", ":P5 console<CR>", { desc = "Toggle console (deprecated)" })
vim.keymap.set("n", "<leader>psd", ":P5 cdp<CR>", { desc = "Toggle CDP DevTools" })
-- Libraries
vim.keymap.set("n", "<leader>pi", ":P5 install ", { desc = "Install library" })
vim.keymap.set("n", "<leader>pu", ":P5 uninstall ", { desc = "Uninstall library" })
vim.keymap.set("n", "<leader>pU", ":P5 sync libs<CR>", { desc = "Update libraries" })
-- Gist
vim.keymap.set("n", "<leader>pg", ":P5 gist ", { desc = "Create gist" })
vim.keymap.set("n", "<leader>pgg", ":P5 gist sync<CR>", { desc = "Sync gist" })
-- Docs
vim.keymap.set("n", "<leader>pd", ":P5 docs<CR>", { desc = "Open p5.js docs" })Symptoms: Running :P5 server shows an error or nothing happens.
Solutions:
-
Ensure Python 3 is installed:
python3 --version
-
Check if the port is already in use:
lsof -i :8000
-
Try a different port:
:P5 server 8080- Check Neovim notifications for specific error messages
Library installation fails or downloads timeout.
Solutions:
-
Ensure curl is installed:
curl --version
-
Check internet connection:
curl -I https://cdnjs.cloudflare.com
-
Verify firewall isn't blocking
localhostconnections -
Check that you're in a valid sketchspace (has
p5.json)
:P5 gist or :P5 sync gist shows an error.
Solutions:
-
Ensure GitHub CLI is installed:
gh --version
-
Authenticate with GitHub:
gh auth login
-
Verify gist URL in
p5.jsonis valid::edit p5.json -
For sync failures, the gist may have been deleted - run
:P5 gistto create a new one
:P5 install or :P5 uninstall shows an error.
Solutions:
-
Verify you're in a sketchspace (directory with a
p5.jsonfile):ls p5.json
-
Check assets/libs directory is writable:
ls -la assets/libs
-
Run setup first to initialize:
:P5 setup
(c) 2026, prjctimg
This is free software, released under the GPL-3.0 license.

