Skip to content

Updated the complete “Speak with Your Hands” tutorial to use the modern ml5.js HandPose API.Speak with your hands 2.0#1509

Open
colemanliyah wants to merge 3 commits into
processing:mainfrom
colemanliyah:speak-with-your-hands-2.0
Open

Updated the complete “Speak with Your Hands” tutorial to use the modern ml5.js HandPose API.Speak with your hands 2.0#1509
colemanliyah wants to merge 3 commits into
processing:mainfrom
colemanliyah:speak-with-your-hands-2.0

Conversation

@colemanliyah

@colemanliyah colemanliyah commented Jul 11, 2026

Copy link
Copy Markdown

Resolves PR #1483 (#1483)

  • Replaced all older ml5 examples (ml5.handpose(), modelReady(), predict events, annotations) with the new API:

    • await ml5.handPose()
    • detectStart()
    • keypoints
  • Updated every example sketch to work with the current ml5 version:

    • Detecting hand keypoints
    • Printing prediction data
    • Drawing fingertips
    • Adding images to fingertips
    • Moving objects with fingers
    • Controlling multiple objects with multiple fingers
    • Using conditionals with finger positions
    • Creating gesture-based interactions
  • Embedded all example sketches directly into the tutorial using instead of linking out to external sketches.

  • Added updated comments and explanations to match the new code structure.

  • Updated explanations of prediction data to match the new HandPose output format (keypoints, keypoints3D, handedness, confidence).

<meta charset="UTF-8" />
<title>Handpose with Webcam</title>

<script src="https://cdn.jsdelivr.net/npm/p5@2.0.5/lib/p5.js"></script>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to use p5@2 latest rather than a specific patch?

createCanvas(400, 350);

// Load Image
img = await loadImage("https://raw.githubusercontent.com/ml5js/ml5-library/49b9bf2/examples/p5js/Handpose/Handpose_Image/data/hand.jpg");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a relative url to one of the assets you added?

@ksen0 ksen0 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A fe minor comments, but thanks so much for going through and updating everything! @doradocodes if you verify locally as well please feel free to merge.

createCanvas(400, 350);

// Load the image and wait until it finishes loading
img = await loadImage("https://raw.githubusercontent.com/ml5js/ml5-library/49b9bf2/examples/p5js/Handpose/Handpose_Image/data/hand.jpg");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here as above

@shiffman shiffman left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @colemanliyah thanks for this wonderful work. I made some small comments throughout. Some of my comments apply to the subsequent examples too but I only noted it for the first instance of a change.

Also, the ml5.js style is to use a variable called hands rather than predictions. That might be a little friendlier to the reader, but it's also ok for this tutorial to have its own code style if you prefer to keep it as predictions.

Let me know if you have any questions!

Comment on lines +84 to +86
async function setup() {
// Create a canvas that's at least the size of the image.
createCanvas(400, 350);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small thing, but noting that the code isn't indented here.

// we use await to wait until the model is ready before continuing.
handPose = await ml5.handPose();

frameRate(1); // Set the frameRate to 1 since we don't need it to run quickly in this case.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this example incorporates noLoop() setting the frame rate to 1 seems extraneous to me.

Comment on lines +100 to +102
handPose.detect(img, results => {
predictions = results;
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a single image this can actually be

Suggested change
handPose.detect(img, results => {
predictions = results;
});
predictions = await handPose.detect(img);


fill(0, 255, 0);
noStroke();
ellipse(keypoint.x, keypoint.y, 10, 10);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

circle() is a little friendlier!

Suggested change
ellipse(keypoint.x, keypoint.y, 10, 10);
circle(keypoint.x, keypoint.y, 10);

Comment on lines +197 to +202
try {
handPose = await ml5.handPose();
console.log("HandPose loaded!");
} catch (error) {
console.error(error);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might skip the try catch here for simplicity.

Comment on lines +280 to +281
- `finger.points[3][0]` corresponds to the x-coordinate of the point 
- `finger.points[3][1]` corresponds to the y-coordinate of the point 

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these should be .x and .y instead of [0] and [1]?


Let's change this code to add hats on each fingertip. [Full Sketch](https://editor.p5js.org/colemanliyah/sketches/pjzyE3gCp) Here are the steps:

1. Add a variable to store the hat image and load it using the `preload()` function.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change to loading with await!

- Replace the `drawKeypoints()` function with a new `drawObject()` function.
- In `drawObject()`, access the index fingertip by selecting the last point of the index finger:
```js
let tip = prediction.keypoints.slice(4, 8)[3];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't comment on this for the previous examples b/c of the createFinger() method uses the arrays in an elegant way. However, here it would make a lot more sense to use the named points inside of the prediction object.

Suggested change
let tip = prediction.keypoints.slice(4, 8)[3];
let tip = prediction.index_finger_tip;

Comment on lines +515 to +517
handPose.detectStart(video, results => {
predictions = results;
});

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use a gotHands(results) callback here to match the ml5.js example style.

Comment on lines +677 to +679
let indexTip = prediction.keypoints.slice(4, 8)[3];
let middleTip = prediction.keypoints.slice(8, 12)[3];
let ringTip = prediction.keypoints.slice(12, 16)[3];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here, use the named points directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants