| <!DOCTYPE html> |
| <html> |
| <head> |
| <title>Pose Recognition with Audio</title> |
| <style> |
| body { font-family: sans-serif; text-align: center; background: #f0f2f5; } |
| canvas { border: 4px solid #333; border-radius: 10px; margin-top: 20px; } |
| button { padding: 10px 25px; font-size: 18px; cursor: pointer; background: #007bff; color: white; border: none; border-radius: 5px; } |
| #label-container { margin-top: 20px; font-weight: bold; font-size: 20px; } |
| </style> |
| </head> |
| <body> |
| <h2>Teachable Machine Pose Model + Audio</h2> |
| <button type="button" onclick="init()">Mulai Kamera</button> |
| <div><canvas id="canvas"></canvas></div> |
| <div id="label-container"></div> |
|
|
| <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.3.1/dist/tf.min.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/@teachablemachine/pose@0.8/dist/teachablemachine-pose.min.js"></script> |
| <script type="text/javascript"> |
| const URL = "https://teachablemachine.withgoogle.com/models/irrvgnfP0/"; |
| let model, webcam, ctx, labelContainer, maxPredictions; |
| |
| const audioFiles = { |
| "kicawkicaw": new Audio('kicawkicaw.mp3'), |
| "semangat": new Audio('semangat.mp3'), |
| "berjuang": new Audio('berjuang.mp3'), |
| "sukses": new Audio('sukses.mp3'), |
| "hidup jokowi": new Audio('hidup jokowi.mp3') |
| }; |
| |
| let lastPlayedClass = ""; |
| |
| async function init() { |
| const modelURL = URL + "model.json"; |
| const metadataURL = URL + "metadata.json"; |
| model = await tmPose.load(modelURL, metadataURL); |
| maxPredictions = model.getTotalClasses(); |
| |
| const size = 400; |
| const flip = true; |
| webcam = new tmPose.Webcam(size, size, flip); |
| await webcam.setup(); |
| await webcam.play(); |
| window.requestAnimationFrame(loop); |
| |
| const canvas = document.getElementById("canvas"); |
| canvas.width = size; canvas.height = size; |
| ctx = canvas.getContext("2d"); |
| labelContainer = document.getElementById("label-container"); |
| for (let i = 0; i < maxPredictions; i++) { |
| labelContainer.appendChild(document.createElement("div")); |
| } |
| } |
| |
| async function loop(timestamp) { |
| webcam.update(); |
| await predict(); |
| window.requestAnimationFrame(loop); |
| } |
| |
| async function predict() { |
| const { pose, posenetOutput } = await model.estimatePose(webcam.canvas); |
| const prediction = await model.predict(posenetOutput); |
| |
| for (let i = 0; i < maxPredictions; i++) { |
| const className = prediction[i].className; |
| const probability = prediction[i].probability; |
| labelContainer.childNodes[i].innerHTML = className + ": " + (probability * 100).toFixed(0) + "%"; |
| |
| if (probability > 0.90 && lastPlayedClass !== className) { |
| playVoice(className); |
| lastPlayedClass = className; |
| } |
| } |
| drawPose(pose); |
| } |
| |
| function playVoice(className) { |
| Object.values(audioFiles).forEach(audio => { |
| audio.pause(); |
| audio.currentTime = 0; |
| }); |
| if (audioFiles[className]) { |
| audioFiles[className].play().catch(e => console.log("Audio play blocked")); |
| } |
| } |
| |
| function drawPose(pose) { |
| if (webcam.canvas) { |
| ctx.drawImage(webcam.canvas, 0, 0); |
| if (pose) { |
| const minPartConfidence = 0.5; |
| tmPose.drawKeypoints(pose.keypoints, minPartConfidence, ctx); |
| tmPose.drawSkeleton(pose.keypoints, minPartConfidence, ctx); |
| } |
| } |
| } |
| </script> |
| </body> |
| </html> |