diff --git a/roshambo/index.html b/roshambo/index.html
new file mode 100644
index 0000000..4434803
--- /dev/null
+++ b/roshambo/index.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+ handtracking
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/roshambo/sketch.js b/roshambo/sketch.js
new file mode 100644
index 0000000..6ae51a6
--- /dev/null
+++ b/roshambo/sketch.js
@@ -0,0 +1,266 @@
+let video;
+let handPose;
+let hands = [];
+let hand;
+let handVals;
+let fingers;
+let handBounds;
+let folders = {};
+let painting;
+let connections = [
+ [0, 1],
+ [1, 2],
+ [2, 3],
+ [3, 4],
+ [1, 5],
+ [5, 6],
+ [6, 7],
+ [7, 8],
+ [13, 9],
+ [9, 5],
+ [2, 5],
+ [9, 10],
+ [10, 11],
+ [11, 12],
+ [17, 13],
+ [13, 14],
+ [14, 15],
+ [15, 16],
+ [0, 17],
+ [17, 18],
+ [18, 19],
+ [19, 20]
+];
+let clearStart = 0;
+let videoScale;
+
+let conf = {
+ hands: {
+ palm_kps: [0, 1, 5, 9, 13, 17],
+ palm_kps_str: null,
+ Left: "#ff0000",
+ Right: "#0000ff",
+ show: true,
+ paint: true,
+ },
+ canvas: {
+ bg: "#00000000"
+ },
+ kps: {
+ size: 8,
+ color: "#000000",
+ draw: false,
+ },
+ kp_conn: {
+ connect: true,
+ stroke: 1,
+ },
+ kp_text: {
+ show: false,
+ size: 8,
+ offx: 10,
+ offy: 5,
+ color: "#000000ff"
+ },
+ camera: {
+ show: true,
+ },
+ hand_bounds: {
+ show: true,
+ stroke: 2
+ }
+
+}
+
+let palm_kps = [0, 1, 5, 17];
+
+function preload() {
+ handPose = ml5.handPose({ flipped: true });
+}
+
+function setup() {
+ createCanvas(window.innerWidth, window.innerHeight);
+ video = createCapture({
+ video: {
+ },
+ audio: false,
+ flipped: true,
+ });
+ videoScale = Math.min(window.innerWidth / 640, window.innerHeight / 480);
+ video.size(640 * videoScale, 480 * videoScale);
+ handPose.detectStart(video, (results) => { hands = results; });
+
+ video.hide();
+
+}
+
+function draw() {
+ clear();
+ strokeWeight(1);
+ stroke(255);
+ fill(conf.canvas.bg);
+ rect(0, 0, width, height);
+ rect(0, 0, video.width, video.height);
+
+ image(video, 0, 0);
+
+ handBounds = {};
+
+ if (hands.length > 0) {
+ hand = hands[0];
+ let index = {
+ x: hand.index_finger_tip.x,
+ y: hand.index_finger_tip.y
+ }
+ let middle = {
+ x: hand.middle_finger_tip.x,
+ y: hand.middle_finger_tip.y
+ }
+ let ring = {
+ x: hand.ring_finger_tip.x,
+ y: hand.ring_finger_tip.y
+ }
+ let pinky = {
+ x: hand.pinky_finger_tip.x,
+ y: hand.pinky_finger_tip.y
+ }
+ let thumb = {
+ x: hand.thumb_tip.x,
+ y: hand.thumb_tip.y
+ }
+ fingers = [thumb, index, middle, ring, pinky];
+
+ handVals = {
+ fingers: fingers,
+ handBounds: getBounds(hand.keypoints),
+ handRad: null,
+ palmBounds: getBounds(palm_kps.map(i => hand.keypoints[i])),
+ palmRad: null,
+ fingersBounds: getBounds(fingers),
+ fingersRad: null,
+
+
+ fingersInPalm: null,
+ fingersOnBounds: 0,
+ handedness: hand.handedness,
+ pose: "none",
+ }
+
+ handVals.palmRad = dist(handVals.palmBounds.min.x, handVals.palmBounds.min.y, handVals.palmBounds.max.x, handVals.palmBounds.max.y) / 2;
+ handVals.handRad = dist(handVals.handBounds.min.x, handVals.handBounds.min.y, handVals.handBounds.max.x, handVals.handBounds.max.y) / 2;
+ handVals.fingersRad = dist(handVals.fingersBounds.min.x, handVals.fingersBounds.min.y, handVals.fingersBounds.max.x, handVals.fingersBounds.max.y) / 2;
+
+ handVals.fingersInPalm = fingers.filter(f => dist(f.x, f.y, handVals.palmBounds.c.x, handVals.palmBounds.c.y) < handVals.palmRad * 1.2).length;
+ handVals.fingersOnBounds = fingers.filter(f => dist(f.x, f.y, handVals.fingersBounds.c.x, handVals.fingersBounds.c.y) < handVals.fingersRad * 1.2).length;
+
+ loopKP(hand, handVals);
+ // drawConnections(hand);
+ // drawHandText(hand);
+ drawBounds(handVals);
+ detectGestures(handVals);
+
+ debug.innerHTML = `
+ Hands detected: ${hands.length}
+ Palm radius: ${handVals.palmRad.toFixed(2)}
+ Fingers in palm: ${handVals.fingersInPalm}
+ Hand radius: ${handVals.handRad.toFixed(2)}
+ Fingers on edge: ${handVals.fingersOnBounds}
+ Pose: ${handVals.pose}
+ `;
+
+ }
+
+}
+
+function drawConnections(hand) {
+ for (let j = 0; j < connections.length; j++) {
+ let pointAIndex = connections[j][0];
+ let pointBIndex = connections[j][1];
+ let pointA = hand.keypoints[pointAIndex];
+ let pointB = hand.keypoints[pointBIndex];
+ stroke(conf.hands[hand.handedness]);
+ strokeWeight(conf.kp_conn.stroke);
+ line(pointA.x, pointA.y, pointB.x, pointB.y);
+ }
+}
+
+function detectGestures(handVals) {
+ if (handVals.fingersInPalm >= 4) {
+ stroke(0, 255, 0);
+ fill(0, 255, 0, 100);
+ // circle(handVals.palmBounds.c.x, handVals.palmBounds.c.y, handVals.palmRad * 2);
+ handVals.pose = "🪨";
+ } else if (handVals.fingersOnBounds == 2 && handVals.fingersInPalm >= 2) {
+ // && dist(handVals.fingers[0].x, handVals.fingers[0].y, handVals.palmBounds.c.x, handVals.palmBounds.c.y) < handVals.palmRad * 1.2
+ stroke(255, 0, 0);
+ fill(255, 0, 0, 100);
+
+ handVals.pose = "✂️";
+ } else if (handVals.fingersInPalm == 0 && handVals.fingersOnBounds >= 3) {
+ handVals.pose = "📄";
+ }
+
+ textSize(64);
+ fill(conf.hands[handVals.handedness]);
+ text(handVals.pose, handVals.palmBounds.c.x - textWidth(handVals.pose) / 2, handVals.palmBounds.c.y);
+}
+
+function drawBounds(handVals) {
+ let x = handVals.handBounds.min.x;
+ let y = handVals.handBounds.min.y;
+ let w = handVals.handBounds.max.x - x;
+ let h = handVals.handBounds.max.y - y;
+
+ noFill();
+ stroke(conf.hands[handVals.handedness]);
+ strokeWeight(conf.hand_bounds.stroke);
+ // rect(x, y, w, h);
+
+ // circle(handVals.palmBounds.c.x, handVals.palmBounds.c.y, handVals.palmRad * 2);// Palm center
+ // circle(handVals.fingersBounds.c.x, handVals.fingersBounds.c.y, handVals.fingersRad * 2);// Finger center
+ // // rect(handVals.fingersBounds.min.x, handVals.fingersBounds.min.y, handVals.fingersBounds.max.x - handVals.fingersBounds.min.x, handVals.fingersBounds.max.y - handVals.fingersBounds.min.y);// Finger bounds
+ // // rect(handVals.palmBounds.min.x, handVals.palmBounds.min.y, handVals.palmBounds.max.x - handVals.palmBounds.min.x, handVals.palmBounds.max.y - handVals.palmBounds.min.y);// Palm bounds
+}
+
+function loopKP(hand, handVals) {
+ for (let j = 0; j < hand.keypoints.length; j++) {
+ const kp = hand.keypoints[j];
+ fill(conf.kps.color);
+ noStroke();
+ if (conf.kps.draw) circle(kp.x, kp.y, conf.kps.size);
+
+ fill(conf.kp_text.color);
+ textSize(conf.kp_text.size);
+ // text(`[${j}]`, kp.x + conf.kp_text.offx, kp.y - conf.kp_text.offy);
+
+
+ }
+ handVals.fingersOnBounds = fingers.filter(f => f.x == handVals.handBounds.min.x || f.x == handVals.handBounds.max.x || f.y == handVals.handBounds.min.y || f.y == handVals.handBounds.max.y).length;
+
+}
+
+function drawHandText(hand) {
+ fill(conf.hands[hand.handedness]);
+ textSize(conf.kp_text.size * 1.5);
+
+ let avgX = conf.hands.palm_kps.reduce((sum, i) => sum + hand.keypoints[i].x, 0) / conf.hands.palm_kps.length;
+ let avgY = conf.hands.palm_kps.reduce((sum, i) => sum + hand.keypoints[i].y, 0) / conf.hands.palm_kps.length;
+ let offset = (conf.kp_text.size * 1.5) / 2;
+ text(hand.handedness, avgX - textWidth(hand.handedness) / 2, avgY + offset);
+}
+function getBounds(arr) {
+ let b = { min: { x: Infinity, y: Infinity }, max: { x: -Infinity, y: -Infinity }, c: { x: 0, y: 0 } };
+ for (let i of arr) {
+ if (i.x < b.min.x) b.min.x = i.x
+ if (i.y < b.min.y) b.min.y = i.y
+ if (i.x > b.max.x) b.max.x = i.x
+ if (i.y > b.max.y) b.max.y = i.y
+ }
+ b.c.x = (b.min.x + b.max.x) / 2;
+ b.c.y = (b.min.y + b.max.y) / 2;
+ return b;
+}
+
+const clamp = (min, num, max) => Math.min(Math.max(num, min), max);
+
+