NakedNous

✨ Welcome to my blog! ✨

I’m Jean Pierre Charalambos, researcher & educator passionate about creating didactic tools through creative coding and visual computing. I build open-source tools to make learning more playful πŸ–₯οΈπŸ“š.

πŸš€ Active Projects

nub p5.quadrille.js p5.platonic p5.tree

🧊 Inactive Projects

maku shaderbase proscene p5.treegl legacy p5.bitboard

πŸŽ“ Teaching Resources

Visual Computing Course OOP Course

πŸ“š Recent Publications

nub JORS Paper p5.quadrille.js SoftwareX Paper
🧘 Yoga practitioner | 🏊 Open water swimmer | πŸ“– Science fiction | 🧠 Philosophy of mind

CPU proximity picking

Zero-GPU hit testing with mouseHit. Ten waypoint nodes around a soft loop β€” each one tested every frame against the cursor by projecting its origin to screen space and comparing against a configurable radius. The hit zone is drawn explicitly with bullsEye, which takes the same size and shape parameters as mouseHit β€” the gizmo is the hit zone, no guessing where the click registers. Use the panel to tune the radius and switch between circular and square hit shapes. ...

April 25, 2026 Β· 4 min Β· Theme PaperMod

GPU color-ID picking

Pixel-perfect mouse picking with mousePick. Fifty mixed primitives β€” boxes, spheres, torii, cones, cylinders β€” drawn at random positions and tagged with a unique integer id encoded as a CSS hex colour by tag. Each frame the scene renders twice: once into a 1Γ—1 framebuffer with each shape filled by its id, then gl.readPixels returns whichever id sits under the cursor; the visible pass renders normally and lights up the hit. Cost is one extra geometry submission per frame regardless of object count, and the answer is exactly the rendered pixel β€” through the hole of a torus, behind a partially-occluding box, anywhere a real fragment landed. ...

April 25, 2026 Β· 5 min Β· Theme PaperMod

Camera interpolation

Lookat-camera keyframe animation with createCameraTrack. A bound animCam plays a four-keyframe track; a separate viewCam orbits around the abstraction β€” eye polyline, gaze rays, per-keyframe mini-camera markers. A live frustum follows playback, and an FBO inset shows what animCam actually sees. Toggle TEX and that inset slides onto the live frustum’s near plane β€” the frustum becomes a window. Two createPanel instances drive the whole thing: one binds checkboxes to UI state, one binds transport controls to the track. ...

April 19, 2026 Β· 7 min Β· Theme PaperMod

Pose interpolation

TRS keyframe animation with createPoseTrack. Four { pos, rot } keyframes, cubic Hermite interpolation of position with auto-Catmull-Rom tangents, and slerp on rotations. A transport panel scrubs the track; a second panel toggles the trackPath overlay bits β€” PATH, CONTROLS, TANGENTS_IN, TANGENTS_OUT β€” and switches interpolation modes live. Unlike createCameraTrack, which applies its result to a camera automatically, PoseTrack produces an interpolated pose you fold into the transform stack yourself with applyPose(track.eval(out)). Four keyframes, one object createPoseTrack returns a PoseTrack β€” a renderer-agnostic state machine for { pos, rot, scl } keyframes. track.add(spec) appends one; adjacent duplicates are skipped by default: ...

April 19, 2026 Β· 5 min Β· Theme PaperMod

Depth-of-field blur with focal target

A depth-of-field blur effect where the focal plane is driven by a live world-space position. The scene is rendered into a p5.Framebuffer, then a p5.strands DOF pass is applied via pipe(). The magenta sphere is the focal target β€” its screen-space z is recomputed every frame with mapLocation() and fed directly into the shader, so the blur follows the sphere continuously. A first-person directional light tracks the viewer using mapDirection(). The shader as a strands callback The DOF pass is authored as a p5.strands callback on baseFilterShader().modify(). There is no raw GLSL string β€” the algorithm is expressed in JavaScript using the strands DSL, which compiles it to WebGL2 under the hood. ...

April 9, 2026 Β· 3 min Β· Theme PaperMod

Post-effects pipeline

Multi-pass post-processing with p5.strands and pipe(). Three filter passes β€” depth-of-field blur, value-noise warp, and pixelation β€” are chained over a scene framebuffer. Each pass is a baseFilterShader().modify() callback; createPanel wires their uniforms automatically. Press 1, 2, or 3 to rotate the pass ordering at runtime. Shader passes as strands callbacks Each post-processing pass is a baseFilterShader().modify() callback β€” a plain JavaScript function using the p5.strands DSL. The framework compiles each callback to WebGL2 at startup; no raw GLSL strings are needed. ...

April 9, 2026 Β· 2 min Β· Theme PaperMod

Toon shading

Toon shading, or cel shading, gives 3D geometry a flat, cartoon-like look by quantizing diffuse reflection into a finite number of discrete shades. In this p5.js v2 version the shader is written as a baseMaterialShader().modify() hook using the p5.strands DSL β€” no raw GLSL strings, no hand-written vertex shader. createPanel wires the color and shades controls to the shader automatically each frame. Toon shader The combineColors hook in baseMaterialShader().modify() receives the eye-space vNormal varying directly β€” no vertex shader boilerplate required. Diffuse intensity is the dot product of the surface normal and the (normalized) light direction; it is then snapped into discrete bands to produce the cel-shading look. ...

April 9, 2026 Β· 3 min Β· Theme PaperMod

3D Brush Painting in VR

A 3D brush painting sketch that uses depth control for VR-style experiences. mapLocation() converts screen-space mouse coordinates and a depth slider value directly into world-space positions, so each brush stroke is placed precisely in 3D. Press r to toggle recording, c to clear, f to re-focus the camera on the world origin. Screen-to-world mapping with mapLocation The core of the brush is a single mapLocation call that lifts the 2D mouse position β€” together with a depth value from the slider β€” into 3D world space each frame: ...

April 9, 2026 Β· 2 min Β· Theme PaperMod

Visualizing Perspective Transformation to NDC

Perspective projection is a fundamental concept in 3D graphics. The transformation maps a view frustum β€” a truncated pyramid β€” into a cube of Normalized Device Coordinates (NDC), producing the foreshortening effect that makes 3D scenes look convincing. The sketch below morphs a set of cajas (boxes) continuously from world space into NDC space, rendered from a third-person camera that also displays the frustum being transformed. Shaders The morph lives in a single GLSL hook injected into p5’s base shaders through buildColorShader and buildStrokeShader β€” no shader files needed. ...

March 24, 2026 Β· 3 min Β· Theme PaperMod

p5.tree.js

p5.tree is a render pipeline layer for p5.js v2 β€” pose and camera interpolation, coordinate-space conversions between WORLD / EYE / SCREEN / NDC, frustum visibility, HUD, multi-pass post-processing, picking, and declarative control panels. The demo below exercises all of it at once. Under the hood it’s three independent packages: a renderer-agnostic numeric core (@nakednous/tree β€” math, spaces, keyframes, visibility), a lightweight DOM layer (@nakednous/ui β€” sliders, transport), and a p5.js v2 bridge that wires them to the canvas. The dependency direction is strict and one-way β€” the core knows nothing about p5 or the DOM β€” which is what lets the same keyframe interpolation that drives a camera path also animate any object, and lets the whole stack run headless or in a future renderer without touching the math. ...

February 17, 2026 Β· 7 min Β· Theme PaperMod