
Created 2025-09-21
/** * ============================================================================ * = ASCII Hands with Ml5.js HandPose = * ============================================================================ * * Wave your hands to see the effect! * * This example shows how to use **Ml5** HandPose to detect hands and draw them * on the screen. Also, it shows how to use **p5** to create a capture video * element and remove it after the video is no longer needed. * * Note that we use ml5 **async constructor** to create the handPose object, * which is what I worked on this summer as a ml5 researcher to support p5 2.0 * async setup. The async constructor is a new feature in ml5 1.0.0. * * You can check out the original pull request here: * https://github.com/ml5js/ml5-next-gen/pull/258 * * You can check out the original issue here: * https://github.com/ml5js/ml5-next-gen/issues/244 */ const width = 96; const height = 30; const scaleX = d3.scaleLinear([0, 640], [0, width]); const scaleY = d3.scaleLinear([0, 480], [0, height]); const video = await createCapture(640, 480); const handPose = await ml5.handPose(video); // Async constructor!!! //➜ ................................................................................................ //➜ .............@.................................................................................. //➜ ....................@........................................................................... //➜ ....@.......@................................................................................... //➜ ....@..............@.........@..........................@....................................... //➜ ...........@..................................................@................................. //➜ ....@.............@........@...................@.........@....@................................. //➜ ........................@.............................................@......................... //➜ ................................................@........@....@................................. //➜ ....@....@.....@.................................@...................@.......................... //➜ ...................@................................................@........................... //➜ ..................................@......................@...................................... //➜ .....................................................@.......@.................................. //➜ .............................@...................................@.............................. //➜ ................................................................................................ //➜ ......................@................@...@.................................................... //➜ ..............@................................................................................. //➜ ....@............................................@.............................................. //➜ ......................................................@......................................... //➜ .............................................................@.................................. //➜ ................................................................................................ //➜ ................................................................................................ //➜ ................................................................................................ //➜ ................................................................................................ //➜ ................................................................................................ //➜ ................................................................................................ //➜ ................................................................................................ //➜ ................................................................................................ //➜ ................................................................................................ //➜ ................................................................................................ handPose.detectStart(video, (hands) => { const buffer = d3.range(width * height).map(() => "."); for (let i = 0; i < hands.length; i++) { const hand = hands[i]; for (let j = 0; j < hand.keypoints.length; j++) { const points = hand.keypoints[j]; const x = ~~scaleX(points.x); const y = ~~scaleY(points.y); if (x > 0 && x < width && y > 0 && y < height) buffer[y * width + x] = "@"; } } let output = ""; for (let i = 0; i < height; ++i) { for (let j = 0; j < width; ++j) output += buffer[i * width + j]; output += i === height - 1 ? "" : "\n"; } clear(); echo(output); }); { invalidation.then(() => removeCapture(video)); } // Use p5 to create a capture video element. function createCapture(width, height) { return new Promise((resolve) => { new p5((p) => { p.setup = () => { p.noCanvas(); p.noLoop(); const video = p.createCapture(p.VIDEO); video.size(width, height); video.hide(); resolve(video); }; }); }); } // Remove the capture video element. function removeCapture(video) { if (video) { if (video.elt && video.elt.srcObject) { video.elt.srcObject.getTracks().forEach((track) => track.stop()); } video.remove(); } } const ml5 = await recho.require("https://unpkg.com/ml5@1/dist/ml5.js"); const p5 = await recho.require("https://unpkg.com/p5@1.2.0/lib/p5.js"); const d3 = await recho.require("d3");