A deep dive into how `grafgameN_8_5-5d.html` translates physical hand movements into game inputs using Computer Vision.
The game relies on two specific libraries provided by Google's TensorFlow team. These are loaded via Content Delivery Network (CDN) to run directly in the browser without backend servers.
The machine learning engine that allows hardware acceleration (WebGL) in the browser. It handles the heavy math required for neural networks.
<script src=".../tfjs"></script>
A pre-trained neural network specifically designed to detect 21 3D landmarks of a human hand from a single video frame.
<script src=".../handpose"></script>
handpose.load(). Once loaded, it continuously passes the webcam video feed to the model to get real-time predictions.
The code doesn't just track "a hand." It specifically stabilizes the input by averaging specific finger joints (Knuckles).
The estimateHands(video) function returns an array of landmarks. The code extracts the MCP (Metacarpophalangeal) joints—the knuckles where the fingers meet the palm.
To prevent "jitter" (shaky movement), the code averages the X and Y coordinates of these three joints to create a single Control Point.
const landmarkIndices = [5, 9, 13];
let totalX = 0, totalY = 0;
// Average the position
controlPointX = totalX / 3;
controlPointY = totalY / 3;
The core innovation in this file is the History Buffer. It doesn't look at where the hand is; it looks at where the hand was compared to now.
A variable controlPointHistory stores positions with timestamps. Old data (>500ms) is constantly removed.
The code calculates the difference (Delta) between the Oldest point in memory and the Current point.
// Note: X is mirrored!
diffX = oldestPoint.x - currentPoint.x;
diffY = currentPoint.y - oldestPoint.y;
const threshold = 25; // Pixels
The code determines the dominant axis (Horizontal vs Vertical) to decide the game input.
IDLE (0)
abs(diffX) > abs(diffY)?
The diagram below simulates how the code interprets a physical hand swipe. The Red Line represents the controlPointHistory (the vector).
Finally, the code injects this logic into the game loop using a non-blocking approach.
requestAnimationFrame(gameLoop);
function gameLoop(now) {
// 1. Run CV (Promise based)
model.estimateHands(video).then( preds => {
direction = getMotionDirection(now);
// Persistent Movement Logic
if (direction !== 0) {
lastValidDirection = direction;
arah_gelinding = direction;
}
});
// 2. Update Game State
// Moves the ball based on 'arah_gelinding'
}
Critical Detail: The variable lastValidDirection ensures that if the camera loses track of the hand momentarily, or the user stops moving, the ball continues rolling in the last intended direction.