{"id":4405,"date":"2026-06-03T00:32:29","date_gmt":"2026-06-02T22:32:29","guid":{"rendered":"https:\/\/app.taekwon-book.com\/train-by-belt\/white-belt\/games\/10th-game-writing-taekwon-do\/"},"modified":"2026-09-19T00:39:46","modified_gmt":"2026-09-18T22:39:46","slug":"10th-game-writing-taekwon-do","status":"publish","type":"page","link":"https:\/\/app.taekwon-book.com\/en\/train-by-belt\/white-belt\/games\/10th-game-writing-taekwon-do\/","title":{"rendered":"10th Game: Writing Taekwon-Do"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"4405\" class=\"elementor elementor-4405 elementor-76\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-4494bce1 e-flex e-con-boxed e-con e-parent\" data-id=\"4494bce1\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;position&quot;:&quot;absolute&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-24a0aac0 bouton-audio-lien elementor-widget elementor-widget-button\" data-id=\"24a0aac0\" data-element_type=\"widget\" data-e-type=\"widget\" data-audio=\"https:\/\/taekwon-book.com\/wp-content\/themes\/hello-elementor-child\/app\/sons\/general\/son_click.mp3\" data-widget_type=\"button.default\">\n\t\t\t\t\t\t\t\t\t\t<a class=\"elementor-button elementor-button-link elementor-size-sm\" href=\"https:\/\/app.taekwon-book.com\/en\/train-by-belt\/white-belt\/\">\n\t\t\t\t\t\t<span class=\"elementor-button-content-wrapper\">\n\t\t\t\t\t\t<span class=\"elementor-button-icon\">\n\t\t\t\t<svg aria-hidden=\"true\" class=\"e-font-icon-svg e-fas-arrow-left\" viewBox=\"0 0 448 512\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\"><path d=\"M257.5 445.1l-22.2 22.2c-9.4 9.4-24.6 9.4-33.9 0L7 273c-9.4-9.4-9.4-24.6 0-33.9L201.4 44.7c9.4-9.4 24.6-9.4 33.9 0l22.2 22.2c9.5 9.5 9.3 25-.4 34.3L136.6 216H424c13.3 0 24 10.7 24 24v32c0 13.3-10.7 24-24 24H136.6l120.5 114.8c9.8 9.3 10 24.8.4 34.3z\"><\/path><\/svg>\t\t\t<\/span>\n\t\t\t\t\t\t\t\t\t<span class=\"elementor-button-text\">Back<\/span>\n\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-590aac8d bloc_case e-flex e-con-boxed e-con e-parent\" data-id=\"590aac8d\" data-element_type=\"container\" data-e-type=\"container\" id=\"bloc_question_1\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-76b071fa e-flex e-con-boxed e-con e-child\" data-id=\"76b071fa\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-21530f18 e-con-full e-flex e-con e-child\" data-id=\"21530f18\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-5c335f30 elementor-widget elementor-widget-heading\" data-id=\"5c335f30\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h1 class=\"elementor-heading-title elementor-size-default\">Write Taekwon-Do<\/h1>\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-3d4535ca elementor-absolute star_page elementor-widget elementor-widget-image\" data-id=\"3d4535ca\" data-element_type=\"widget\" data-e-type=\"widget\" data-settings=\"{&quot;_position&quot;:&quot;absolute&quot;}\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"35\" height=\"33\" src=\"https:\/\/app.taekwon-book.com\/wp-content\/uploads\/2026\/06\/etoile_ecran_off.svg\" class=\"attachment-large size-large wp-image-4320\" alt=\"\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-755e52da e-con-full e-flex e-con e-child\" data-id=\"755e52da\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t<div class=\"elementor-element elementor-element-c7f0cff e-flex e-con-boxed e-con e-child\" data-id=\"c7f0cff\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-421b0454 e-con-full e-flex e-con e-child\" data-id=\"421b0454\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-24ff9b8b elementor-widget elementor-widget-html\" data-id=\"24ff9b8b\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t\r\n\r\n<div class=\"tkd-trace-game\">\r\n\r\n    <!-- SVG mod\u00e8le (guide gris, inchang\u00e9 visuellement) -->\r\n    <div class=\"svg-container\">\r\n        <img decoding=\"async\" id=\"svgModel\" src=\"https:\/\/taekwon-book.com\/wp-content\/themes\/hello-elementor-child\/app\/img\/hangeul\/taekwon_icon_tae.svg\" alt=\"Taekwondo symbol\">\r\n    <\/div>\r\n\r\n    <!-- Calque de \"coloriage\" : se colore en vert l\u00e0 o\u00f9 le trac\u00e9 est correct -->\r\n    \r\n\r\n    <!-- Canvas dessin (trait de l'utilisateur) -->\r\n    \r\n\r\n    <div class=\"controls\">\r\n        <button id=\"resetBtn\">Clear<\/button>\r\n    <\/div>\r\n\r\n    <div id=\"message\"><\/div>\r\n\r\n<\/div>\r\n\r\n\r\n\r\n.valid_write{\r\n    visibility:hidden;\r\n    opacity:0;\r\n    transition:0.3s;\r\n}\r\n\r\n.tkd-trace-game{\r\n    position:relative;\r\n    width:340px;\r\n    margin:20px auto;\r\n    font-family:Arial, sans-serif;\r\n    text-align:center;\r\n    max-height:370px;\r\n}\r\n\r\n.svg-container{\r\n    position:absolute;\r\n    inset:0;\r\n    display:flex;\r\n    align-items:center;\r\n    justify-content:center;\r\n    pointer-events:none;\r\n    user-select:none;\r\n    height:340px;\r\n    z-index:3;\r\n}\r\n\r\n.svg-container img{\r\n    width:240px;\r\n    opacity:0.35;\r\n}\r\n\r\n#fillCanvas{\r\n    position:absolute;\r\n    top:0;\r\n    left:0;\r\n    width:340px;\r\n    \/* \u26a0\ufe0f Doit rester \u00e9gal \u00e0 la r\u00e9solution interne du canvas (340x340, voir\r\n       fillCanvas.width\/height plus bas) et \u00e0 la hauteur de .svg-container.\r\n       Une valeur diff\u00e9rente ici \u00e9tire verticalement tout ce qui est dessin\u00e9\r\n       sur le canvas par rapport au mod\u00e8le gris, ce qui donnait l'impression\r\n       d'un trac\u00e9 \"d\u00e9cal\u00e9 vers le bas\". *\/\r\n    height:340px;\r\n    z-index:2;\r\n    pointer-events:none;\r\n    box-sizing:border-box;\r\n}\r\n\r\n#traceCanvas{\r\n    position:relative;\r\n    z-index:1;\r\n    width:340px;\r\n    \/* M\u00eame remarque que #fillCanvas ci-dessus : doit rester 340px. *\/\r\n    height:340px;\r\n    border:3px solid #38656d;\r\n    border-radius:15px;\r\n    background:#FCFCFC;\r\n    touch-action:none;\r\n    box-sizing:border-box;\r\n}\r\n\r\n.controls{\r\n    margin-top:15px;\r\n    position:absolute;\r\n    bottom:10px;\r\n    right:10px;\r\n    z-index:10;\r\n}\r\n\r\n#resetBtn{\r\n    font-size:16px;\r\n    padding:12px;\r\n    border:none;\r\n    border-radius:10px;\r\n    background:#41828d;\r\n    color:white;\r\n    cursor:pointer;\r\n    font-family:\"Kalam\";\r\n    font-weight:bold;\r\n}\r\n\r\n#message{\r\n    margin-top:20px;\r\n    font-size:32px;\r\n    font-weight:bold;\r\n    min-height:40px;\r\n    position: absolute;\r\n    top: 0;\r\n    z-index: 2;\r\n}\r\n\r\n\r\n\r\n\r\n(function () {\r\n'use strict';\r\n\r\n\/\/ =========================\r\n\/\/ CONFIG\r\n\/\/ =========================\r\n\r\n\/\/ \u26a0\ufe0f Les SVG sont maintenant charg\u00e9s depuis app.taekwon-book.com (m\u00eame origine que\r\n\/\/ cette page), et non plus depuis taekwon-book.com. Avant, le navigateur \"taintait\"\r\n\/\/ (contaminait) les canvas d\u00e8s qu'on y dessinait l'image du mod\u00e8le, car\r\n\/\/ taekwon-book.com ne renvoie pas d'en-t\u00eate CORS pour ces fichiers : toute lecture\r\n\/\/ de pixels (hiddenCtx.getImageData \/ toleranceCtx.getImageData, utilis\u00e9es par la\r\n\/\/ validation) provoquait alors une erreur de s\u00e9curit\u00e9 silencieuse, et le jeu ne\r\n\/\/ validait donc jamais un caract\u00e8re, m\u00eame bien trac\u00e9. Les sons restent sur\r\n\/\/ taekwon-book.com (l'audio n'est pas concern\u00e9 par cette restriction).\r\nconst letters = [\r\n    {\r\n        svg: 'https:\/\/app.taekwon-book.com\/wp-content\/plugins\/taekon-app\/assets\/images\/jeux\/ecriture\/hangeul\/taekwon_icon_tae.svg',\r\n        sound: 'https:\/\/taekwon-book.com\/wp-content\/themes\/hello-elementor-child\/app\/sons\/ecriture\/taekwon_son_tae.mp3'\r\n    },\r\n    {\r\n        svg: 'https:\/\/app.taekwon-book.com\/wp-content\/plugins\/taekon-app\/assets\/images\/jeux\/ecriture\/hangeul\/taekwon_icon_kwon.svg',\r\n        sound: 'https:\/\/taekwon-book.com\/wp-content\/themes\/hello-elementor-child\/app\/sons\/ecriture\/taekwon_son_kwon.mp3'\r\n    },\r\n    {\r\n        svg: 'https:\/\/app.taekwon-book.com\/wp-content\/plugins\/taekon-app\/assets\/images\/jeux\/ecriture\/hangeul\/taekwon_icon_do.svg',\r\n        sound: 'https:\/\/taekwon-book.com\/wp-content\/themes\/hello-elementor-child\/app\/sons\/ecriture\/taekwon_son_do.mp3'\r\n    }\r\n];\r\n\r\nconst validationSound =\r\n    'https:\/\/taekwon-book.com\/wp-content\/themes\/hello-elementor-child\/app\/sons\/general\/son_juste.mp3';\r\n\r\nconst finalSound =\r\n    'https:\/\/taekwon-book.com\/wp-content\/themes\/hello-elementor-child\/app\/sons\/general\/son_yaay.mp3';\r\n\r\nconst drawingSound = new Audio(\r\n    'https:\/\/taekwon-book.com\/wp-content\/themes\/hello-elementor-child\/app\/sons\/ecriture\/crayon.mp3'\r\n);\r\n\r\ndrawingSound.loop = true;\r\ndrawingSound.volume = 0.35;\r\n\r\n\/\/ -------------------------------------------------------------------------\r\n\/\/ R\u00c9GLAGES DE VALIDATION (\u00e0 ajuster ici si besoin, apr\u00e8s avoir test\u00e9 avec\r\n\/\/ des vrais enfants) :\r\n\/\/\r\n\/\/ - thresholds[i]        : % minimum du caract\u00e8re qui doit \u00eatre recouvert\r\n\/\/                           pour valider (0 \u00e0 1). Un par caract\u00e8re.\r\n\/\/ - PRECISION_THRESHOLD   : % minimum du trac\u00e9 de l'utilisateur qui doit\r\n\/\/                           tomber pr\u00e8s du caract\u00e8re (pas de gribouillage\r\n\/\/                           tr\u00e8s en dehors des lignes). 0 \u00e0 1.\r\n\/\/ - TOLERANCE_BLUR_PX     : marge de tol\u00e9rance autour du caract\u00e8re (en\r\n\/\/                           pixels) pour la v\u00e9rification de pr\u00e9cision.\r\n\/\/                           Plus grand = plus permissif.\r\n\/\/ - TOLERANCE_ALPHA_CUTOFF: \u00e0 combien de \"flou\" on consid\u00e8re qu'un pixel\r\n\/\/                           est encore \"proche\" du caract\u00e8re (0 \u00e0 255).\r\n\/\/                           Plus petit = plus permissif.\r\n\/\/ -------------------------------------------------------------------------\r\n\r\nconst thresholds = [0.9, 0.9, 0.93];\r\nconst PRECISION_THRESHOLD  = 0.55;\r\nconst TOLERANCE_BLUR_PX    = 14;\r\nconst TOLERANCE_ALPHA_CUTOFF = 40;\r\n\r\n\r\n\/\/ =========================\r\n\/\/ VARIABLES\r\n\/\/ =========================\r\n\r\nlet currentLetter = 0;\r\nlet drawing = false;\r\nlet validated = false;\r\nlet lastPos = null;\r\n\r\nconst svgModel = document.getElementById('svgModel');\r\nconst message  = document.getElementById('message');\r\n\r\nconst canvas = document.getElementById('traceCanvas');\r\nconst ctx    = canvas.getContext('2d');\r\n\r\ncanvas.width  = 340;\r\ncanvas.height = 340;\r\n\r\nctx.lineWidth   = 20;\r\nctx.lineCap     = 'round';\r\nctx.lineJoin    = 'round';\r\nctx.strokeStyle = '#2c4a50';\r\n\r\n\/\/ Calque visible qui se colore en vert l\u00e0 o\u00f9 le caract\u00e8re est bien trac\u00e9\r\nconst fillCanvas = document.getElementById('fillCanvas');\r\nconst fillCtx    = fillCanvas.getContext('2d');\r\nfillCanvas.width  = 340;\r\nfillCanvas.height = 340;\r\n\r\n\/\/ Canvas cach\u00e9 : silhouette pleine du mod\u00e8le (sert au calcul de couverture)\r\nconst hiddenCanvas = document.createElement('canvas');\r\nconst hiddenCtx     = hiddenCanvas.getContext('2d');\r\nhiddenCanvas.width  = 340;\r\nhiddenCanvas.height = 340;\r\n\r\n\/\/ Canvas cach\u00e9 : silhouette \"flout\u00e9e\" du mod\u00e8le (sert au calcul de pr\u00e9cision,\r\n\/\/ c'est-\u00e0-dire \u00e0 d\u00e9tecter si le trac\u00e9 de l'utilisateur reste raisonnablement\r\n\/\/ proche du caract\u00e8re plut\u00f4t que de gribouiller n'importe o\u00f9)\r\nconst toleranceCanvas = document.createElement('canvas');\r\nconst toleranceCtx     = toleranceCanvas.getContext('2d');\r\ntoleranceCanvas.width  = 340;\r\ntoleranceCanvas.height = 340;\r\n\r\n\r\n\/\/ =========================\r\n\/\/ INITIALISATION\r\n\/\/ =========================\r\n\r\nloadCurrentLetter();\r\n\r\nfunction loadCurrentLetter(){\r\n\r\n    svgModel.src = letters[currentLetter].svg;\r\n\r\n    message.innerHTML = '';\r\n\r\n    validated = false;\r\n\r\n    clearCanvas();\r\n\r\n    \/\/ Pr\u00e9parer les canvas de r\u00e9f\u00e9rence apr\u00e8s chargement du SVG\r\n    svgModel.onload = () =&gt; {\r\n\r\n        \/\/ Silhouette pleine (couverture)\r\n        hiddenCtx.clearRect(0, 0, 340, 340);\r\n        hiddenCtx.drawImage(svgModel, 50, 50, 240, 240);\r\n\r\n        \/\/ Silhouette flout\u00e9e (tol\u00e9rance \/ pr\u00e9cision)\r\n        toleranceCtx.clearRect(0, 0, 340, 340);\r\n        toleranceCtx.save();\r\n        toleranceCtx.filter = `blur(${TOLERANCE_BLUR_PX}px)`;\r\n        toleranceCtx.drawImage(svgModel, 50, 50, 240, 240);\r\n        toleranceCtx.restore();\r\n    };\r\n}\r\n\r\n\r\n\/\/ =========================\r\n\/\/ DESSIN\r\n\/\/ =========================\r\n\r\ncanvas.addEventListener('pointerdown', startDrawing);\r\ncanvas.addEventListener('pointermove', draw);\r\ncanvas.addEventListener('pointerup', stopDrawing);\r\ncanvas.addEventListener('pointercancel', stopDrawing);\r\n\r\n\/\/ \u2705 Pas d'\u00e9couteur \"pointerleave\" : avant, le trac\u00e9 s'interrompait d\u00e8s que\r\n\/\/ la souris\/le doigt sortait ne serait-ce qu'un instant du canevas (tr\u00e8s\r\n\/\/ g\u00eanant pr\u00e8s des bords). Avec setPointerCapture() ci-dessous, le canevas\r\n\/\/ continue de recevoir les \u00e9v\u00e9nements m\u00eame quand le pointeur en sort\r\n\/\/ l\u00e9g\u00e8rement ; le trac\u00e9 ne s'arr\u00eate plus que quand l'utilisateur rel\u00e2che\r\n\/\/ r\u00e9ellement (pointerup \/ pointercancel).\r\n\r\nfunction getPosition(e){\r\n\r\n    const rect = canvas.getBoundingClientRect();\r\n\r\n    return {\r\n        x: (e.clientX - rect.left) * (canvas.width \/ rect.width),\r\n        y: (e.clientY - rect.top) * (canvas.height \/ rect.height)\r\n    };\r\n}\r\n\r\nfunction startDrawing(e){\r\n\r\n    if(validated) return;\r\n\r\n    canvas.setPointerCapture(e.pointerId);\r\n\r\n    drawing = true;\r\n\r\n    const pos = getPosition(e);\r\n    lastPos = pos;\r\n\r\n    ctx.beginPath();\r\n    ctx.moveTo(pos.x, pos.y);\r\n\r\n    drawingSound.currentTime = 0;\r\n    drawingSound.play().catch(() =&gt; {});\r\n}\r\n\r\nfunction draw(e){\r\n\r\n    if(!drawing || validated) return;\r\n\r\n    const pos = getPosition(e);\r\n\r\n    \/\/ \u2705 Lissage du trait : on passe par le point m\u00e9dian plut\u00f4t que de\r\n    \/\/ tracer des segments droits bruts (ligne beaucoup moins \"hach\u00e9e\",\r\n    \/\/ surtout perceptible sur les trac\u00e9s rapides au doigt).\r\n    const mid = { x: (lastPos.x + pos.x) \/ 2, y: (lastPos.y + pos.y) \/ 2 };\r\n\r\n    ctx.quadraticCurveTo(lastPos.x, lastPos.y, mid.x, mid.y);\r\n    ctx.stroke();\r\n    ctx.beginPath();\r\n    ctx.moveTo(mid.x, mid.y);\r\n\r\n    paintFill(lastPos, mid);\r\n\r\n    lastPos = pos;\r\n\r\n    checkProgress();\r\n}\r\n\r\nfunction stopDrawing(e){\r\n\r\n    if (!drawing) return;\r\n\r\n    drawing = false;\r\n    drawingSound.pause();\r\n\r\n    if (e &amp;&amp; e.pointerId != null &amp;&amp; canvas.hasPointerCapture?.(e.pointerId)) {\r\n        canvas.releasePointerCapture(e.pointerId);\r\n    }\r\n}\r\n\r\n\r\n\/\/ =========================\r\n\/\/ COLORIAGE DU CARACT\u00c8RE\r\n\/\/ =========================\r\n\/\/ Reproduit l'effet \"le caract\u00e8re se colore au fur et \u00e0 mesure qu'on le\r\n\/\/ trace correctement\" (comme les jeux d'\u00e9criture type Duolingo). On dessine\r\n\/\/ le m\u00eame trait que l'utilisateur, en vert, puis on ne garde que la partie\r\n\/\/ qui tombe \u00e0 l'int\u00e9rieur du caract\u00e8re (destination-in) : impossible de\r\n\/\/ \"peindre en dehors des lignes\".\r\n\r\nfunction paintFill(from, to){\r\n\r\n    fillCtx.globalCompositeOperation = 'source-over';\r\n    fillCtx.strokeStyle = '#43a047';\r\n    fillCtx.lineWidth   = ctx.lineWidth + 4;\r\n    fillCtx.lineCap     = 'round';\r\n    fillCtx.lineJoin    = 'round';\r\n\r\n    fillCtx.beginPath();\r\n    fillCtx.moveTo(from.x, from.y);\r\n    fillCtx.lineTo(to.x, to.y);\r\n    fillCtx.stroke();\r\n\r\n    fillCtx.globalCompositeOperation = 'destination-in';\r\n    fillCtx.drawImage(svgModel, 50, 50, 240, 240);\r\n    fillCtx.globalCompositeOperation = 'source-over';\r\n}\r\n\r\n\r\n\/\/ =========================\r\n\/\/ VALIDATION PAR COUVERTURE + PR\u00c9CISION\r\n\/\/ =========================\r\n\r\nfunction checkProgress(){\r\n\r\n    if(validated) return;\r\n\r\n    const modelData     = hiddenCtx.getImageData(0, 0, 340, 340).data;\r\n    const userData       = ctx.getImageData(0, 0, 340, 340).data;\r\n    const toleranceData = toleranceCtx.getImageData(0, 0, 340, 340).data;\r\n\r\n    let totalModelPixels = 0;\r\n    let matchedPixels    = 0; \/\/ pixels du mod\u00e8le recouverts -&gt; couverture\r\n    let userPixels       = 0; \/\/ pixels dessin\u00e9s par l'utilisateur\r\n    let userPixelsClose  = 0; \/\/ ...parmi eux, ceux proches du caract\u00e8re -&gt; pr\u00e9cision\r\n\r\n    for(let i = 0; i  30){\r\n            totalModelPixels++;\r\n            if(userAlpha &gt; 25) matchedPixels++;\r\n        }\r\n\r\n        \/\/ pixel dessin\u00e9 par l'utilisateur\r\n        if(userAlpha &gt; 25){\r\n            userPixels++;\r\n            if(toleranceData[i + 3] &gt; TOLERANCE_ALPHA_CUTOFF) userPixelsClose++;\r\n        }\r\n    }\r\n\r\n    const coverage  = totalModelPixels ? matchedPixels \/ totalModelPixels : 0;\r\n    const precision = userPixels ? userPixelsClose \/ userPixels : 1;\r\n\r\n    \/\/ DEBUG\r\n    \/\/ console.log('couverture', Math.round(coverage * 100) + '%', 'pr\u00e9cision', Math.round(precision * 100) + '%');\r\n\r\n    \/\/ Seuil validation : il faut \u00e0 la fois avoir recouvert assez du\r\n    \/\/ caract\u00e8re ET \u00eatre rest\u00e9 raisonnablement dans les lignes.\r\n    if (coverage &gt; thresholds[currentLetter] &amp;&amp; precision &gt; PRECISION_THRESHOLD) {\r\n\r\n        validated = true;\r\n\r\n        validateLetter();\r\n    }\r\n}\r\n\r\n\r\n\/\/ =========================\r\n\/\/ VALIDATION\r\n\/\/ =========================\r\n\r\nfunction validateLetter(){\r\n\r\n    drawingSound.pause();\r\n\r\n    \/\/ son validation\r\n    const validateAudio = new Audio(validationSound);\r\n    validateAudio.play().catch(() =&gt; {});\r\n\r\n    message.innerHTML = '\u2b50 Bravo ! \u2b50';\r\n\r\n    \/\/ son symbole\r\n    setTimeout(() =&gt; {\r\n\r\n        const symbolAudio = new Audio(letters[currentLetter].sound);\r\n        symbolAudio.play().catch(() =&gt; {});\r\n\r\n    }, 600);\r\n\r\n    \/\/ prochain symbole\r\n    setTimeout(() =&gt; {\r\n\r\n        nextLetter();\r\n\r\n    }, 1800);\r\n}\r\n\r\n\r\n\/\/ =========================\r\n\/\/ SUIVANT\r\n\/\/ =========================\r\n\r\nfunction nextLetter(){\r\n\r\n    currentLetter++;\r\n\r\n    if(currentLetter &gt;= letters.length){\r\n\r\n        endGame();\r\n\r\n        return;\r\n    }\r\n\r\n    loadCurrentLetter();\r\n}\r\n\r\n\r\n\/\/ =========================\r\n\/\/ FIN\r\n\/\/ =========================\r\n\r\nfunction endGame(){\r\n\r\n    svgModel.style.display = 'none';\r\n\r\n    clearCanvas();\r\n\r\n    message.innerHTML = '<img decoding=\"async\" src=\"https:\/\/taekwon-book.com\/wp-content\/uploads\/2023\/06\/TAEKWON_abonnement.png\"><p>F\u00e9licitations !<\/p>'\r\n\r\n    launchConfetti();\r\n\r\n    setTimeout(() =&gt; {\r\n\r\n        const finalAudio = new Audio(finalSound);\r\n        finalAudio.play().catch(() =&gt; {});\r\n\r\n    }, 150);\r\n\r\n    \/\/ Afficher le bouton final\r\n    const finalBtn = document.querySelector('.valid_write');\r\n\r\n    if(finalBtn){\r\n        finalBtn.style.visibility = 'visible';\r\n        finalBtn.style.opacity = '1';\r\n    }\r\n\r\n    \/\/ Faire appara\u00eetre (scroller vers) le bloc de fin, comme sur les autres\r\n    \/\/ pages de techniques (ex. jeu des paires) qui utilisent le m\u00eame bloc\r\n    \/\/ partag\u00e9 #bloc_question_fin. Ce bloc est une section Elementor commune,\r\n    \/\/ pas g\u00e9n\u00e9r\u00e9e par ce script : il doit exister sur la page pour que ce\r\n    \/\/ scroll ait un effet visible.\r\n    setTimeout(() =&gt; {\r\n\r\n        const sectionFin = document.getElementById('bloc_question_fin');\r\n\r\n        if(sectionFin){\r\n            sectionFin.scrollIntoView({ behavior: 'smooth', block: 'start' });\r\n        }\r\n\r\n    }, 30);\r\n}\r\n\r\n\r\n\/\/ =========================\r\n\/\/ RESET\r\n\/\/ =========================\r\n\r\ndocument.getElementById('resetBtn')\r\n.addEventListener('click', clearCanvas);\r\n\r\nfunction clearCanvas(){\r\n\r\n    ctx.clearRect(0, 0, canvas.width, canvas.height);\r\n    fillCtx.clearRect(0, 0, fillCanvas.width, fillCanvas.height);\r\n}\r\n\r\n\r\n\/\/ =========================\r\n\/\/ CONFETTIS\r\n\/\/ =========================\r\n\r\nfunction launchConfetti(){\r\n\r\n    const duration = 2500;\r\n    const end = Date.now() + duration;\r\n\r\n    const interval = setInterval(() =&gt; {\r\n\r\n        if(Date.now() &gt; end){\r\n\r\n            clearInterval(interval);\r\n\r\n            return;\r\n        }\r\n\r\n        createConfetti();\r\n\r\n    }, 120);\r\n}\r\n\r\nfunction createConfetti(){\r\n\r\n    const confetti = document.createElement('div');\r\n\r\n    const symbols = ['\u2b50','\ud83e\udd4b','\u2728'];\r\n\r\n    confetti.innerHTML =\r\n        symbols[Math.floor(Math.random() * symbols.length)];\r\n\r\n    confetti.style.position = 'fixed';\r\n    confetti.style.left = Math.random() * window.innerWidth + 'px';\r\n    confetti.style.top = '-40px';\r\n    confetti.style.fontSize = (20 + Math.random() * 20) + 'px';\r\n    confetti.style.pointerEvents = 'none';\r\n    confetti.style.zIndex = 9999;\r\n\r\n    document.body.appendChild(confetti);\r\n\r\n    let pos = -40;\r\n\r\n    const fall = setInterval(() =&gt; {\r\n\r\n        pos += 6;\r\n\r\n        confetti.style.top = pos + 'px';\r\n\r\n        if(pos &gt; window.innerHeight){\r\n\r\n            clearInterval(fall);\r\n\r\n            confetti.remove();\r\n        }\r\n\r\n    }, 16);\r\n}\r\n\r\n})();\r\n\r\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-560d2e5f elementor-align-center btn_quiz valid_write elementor-mobile-align-justify elementor-widget elementor-widget-button\" data-id=\"560d2e5f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"button.default\">\n\t\t\t\t\t\t\t\t\t\t<a class=\"elementor-button elementor-button-link elementor-size-sm\" href=\"https:\/\/taekwon-book.com\/parcours-par-ceinture\/ceinture-blanche\/\">\n\t\t\t\t\t\t<span class=\"elementor-button-content-wrapper\">\n\t\t\t\t\t\t\t\t\t<span class=\"elementor-button-text\">Submit<\/span>\n\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-3c31d88a e-flex e-con-boxed e-con e-child\" data-id=\"3c31d88a\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-2efdae86 question_texte elementor-widget elementor-widget-text-editor\" data-id=\"2efdae86\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>Learn how to write Taekwon-Do in Korean! Follow the lines to complete each symbol.<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-f386d56 bloc_case e-flex e-con-boxed e-con e-parent\" data-id=\"f386d56\" data-element_type=\"container\" data-e-type=\"container\" id=\"bloc_question_fin\" data-settings=\"{&quot;background_background&quot;:&quot;gradient&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-eafb395 elementor-widget elementor-widget-html\" data-id=\"eafb395\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div id=\"confetti-wrapper\">\r\n  \r\n<\/div>\r\n\r\n\r\n  #confetti-wrapper {\r\n    position: absolute;\r\n    width: 100%;\r\n    height: 100vh; \/* tu peux ajuster ou mettre 100vh *\/\r\n    overflow: hidden;\r\n  }\r\n\r\n  #confettiCanvas {\r\n    position: absolute;\r\n    top: 0;\r\n    left: 0;\r\n    width: 100%;\r\n    height: 100%;\r\n    pointer-events: none;\r\n  }\r\n\r\n\r\n\r\n(function () {\r\n  const canvas = document.getElementById('confettiCanvas');\r\n  const ctx = canvas.getContext('2d');\r\n\r\n  function resizeCanvas() {\r\n    const wrapper = document.getElementById('confetti-wrapper');\r\n    canvas.width = wrapper.offsetWidth;\r\n    canvas.height = wrapper.offsetHeight;\r\n  }\r\n\r\n  window.addEventListener('resize', resizeCanvas);\r\n  resizeCanvas();\r\n\r\n  let confetti = [];\r\n\r\n  function createConfetti() {\r\n    confetti = [];\r\n    for (let i = 0; i  {\r\n      ctx.fillStyle = c.color;\r\n      ctx.fillRect(c.x, c.y, c.size, c.size * 0.6);\r\n\r\n      c.y += c.speed;\r\n      c.x += c.drift;\r\n\r\n      if (c.y &gt; canvas.height) {\r\n        c.y = -c.size;\r\n        c.x = Math.random() * canvas.width;\r\n      }\r\n    });\r\n  }\r\n\r\n  let running = true;\r\n\r\n  document.addEventListener(\"visibilitychange\", () =&gt; {\r\n    running = !document.hidden;\r\n  });\r\n\r\n  function animate() {\r\n    if (running) drawConfetti();\r\n    requestAnimationFrame(animate);\r\n  }\r\n\r\n  createConfetti();\r\n  animate();\r\n})();\r\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-3c75475 e-con-full e-flex e-con e-child\" data-id=\"3c75475\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b984792 elementor-widget elementor-widget-heading\" data-id=\"b984792\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t\t<h2 class=\"elementor-heading-title elementor-size-default\">Well done!<\/h2>\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-c7c5c47 e-con-full e-flex e-con e-child\" data-id=\"c7c5c47\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-651af03 complete_img elementor-widget elementor-widget-image\" data-id=\"651af03\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img fetchpriority=\"high\" decoding=\"async\" width=\"500\" height=\"347\" src=\"https:\/\/app.taekwon-book.com\/wp-content\/uploads\/2026\/06\/TAEKWON_abonnement.png\" class=\"attachment-large size-large wp-image-4353\" alt=\"\" srcset=\"https:\/\/app.taekwon-book.com\/wp-content\/uploads\/2026\/06\/TAEKWON_abonnement.png 500w, https:\/\/app.taekwon-book.com\/wp-content\/uploads\/2026\/06\/TAEKWON_abonnement-300x208.png 300w\" sizes=\"(max-width: 500px) 100vw, 500px\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-839cbda e-con-full e-flex e-con e-child\" data-id=\"839cbda\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-e09c547 elementor-widget elementor-widget-text-editor\" data-id=\"e09c547\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t\t\t\t\t\t<p>Congratulations on completing the writing workshop!<\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-96be1cb e-con-full e-flex e-con e-child\" data-id=\"96be1cb\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b13dd96 elementor-align-justify elementor-mobile-align-justify bouton-son btn_close_section btn_quiz elementor-widget-mobile__width-inherit elementor-widget elementor-widget-button\" data-id=\"b13dd96\" data-element_type=\"widget\" data-e-type=\"widget\" data-audio=\"https:\/\/taekwon-book.com\/wp-content\/themes\/hello-elementor-child\/app\/sons\/general\/son_click.mp3\" data-widget_type=\"button.default\">\n\t\t\t\t\t\t\t\t\t\t<a class=\"elementor-button elementor-button-link elementor-size-sm\" href=\"https:\/\/app.taekwon-book.com\/en\/train-by-belt\/white-belt\/games\/\">\n\t\t\t\t\t\t<span class=\"elementor-button-content-wrapper\">\n\t\t\t\t\t\t\t\t\t<span class=\"elementor-button-text\">Other games<\/span>\n\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-9270b4d elementor-align-justify elementor-mobile-align-justify bouton-son btn_close_section btn_quiz elementor-widget-mobile__width-inherit elementor-widget elementor-widget-button\" data-id=\"9270b4d\" data-element_type=\"widget\" data-e-type=\"widget\" data-audio=\"https:\/\/taekwon-book.com\/wp-content\/themes\/hello-elementor-child\/app\/sons\/general\/son_click.mp3\" data-widget_type=\"button.default\">\n\t\t\t\t\t\t\t\t\t\t<a class=\"elementor-button elementor-button-link elementor-size-sm\" href=\"https:\/\/app.taekwon-book.com\/en\/\">\n\t\t\t\t\t\t<span class=\"elementor-button-content-wrapper\">\n\t\t\t\t\t\t\t\t\t<span class=\"elementor-button-text\">Back to home<\/span>\n\t\t\t\t\t<\/span>\n\t\t\t\t\t<\/a>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-509840f1 e-con-full e-flex e-con e-parent\" data-id=\"509840f1\" data-element_type=\"container\" data-e-type=\"container\" data-settings=\"{&quot;position&quot;:&quot;absolute&quot;}\">\n\t\t\t\t<div class=\"elementor-element elementor-element-730d7d8c elementor-widget elementor-widget-html\" data-id=\"730d7d8c\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<div id=\"tkd-progress-bar\">\r\n    <p class=\"progression_text\">Progression<\/p>\r\n  <div id=\"tkd-progress-bar-inner\"><\/div>\r\n<\/div>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Back Write Taekwon-Do Clear .valid_write{ visibility:hidden; opacity:0; transition:0.3s; } .tkd-trace-game{ position:relative; width:340px; margin:20px auto; font-family:Arial, sans-serif; text-align:center; max-height:370px; } .svg-container{ position:absolute; inset:0; display:flex; align-items:center; justify-content:center; pointer-events:none; user-select:none; height:340px; z-index:3; } .svg-container img{ width:240px; opacity:0.35; } #fillCanvas{ position:absolute; top:0; left:0; width:340px; \/* \u26a0\ufe0f Doit rester \u00e9gal \u00e0 la r\u00e9solution interne du canvas (340&#215;340, voir fillCanvas.width\/height plus [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":4384,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"_acf_changed":false,"footnotes":""},"categorie-technique":[40],"ceinture":[44],"class_list":["post-4405","page","type-page","status-publish","hentry","categorie-technique-quiz","ceinture-white-belt"],"acf":[],"_links":{"self":[{"href":"https:\/\/app.taekwon-book.com\/en\/wp-json\/wp\/v2\/pages\/4405","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/app.taekwon-book.com\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/app.taekwon-book.com\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/app.taekwon-book.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/app.taekwon-book.com\/en\/wp-json\/wp\/v2\/comments?post=4405"}],"version-history":[{"count":4,"href":"https:\/\/app.taekwon-book.com\/en\/wp-json\/wp\/v2\/pages\/4405\/revisions"}],"predecessor-version":[{"id":6691,"href":"https:\/\/app.taekwon-book.com\/en\/wp-json\/wp\/v2\/pages\/4405\/revisions\/6691"}],"up":[{"embeddable":true,"href":"https:\/\/app.taekwon-book.com\/en\/wp-json\/wp\/v2\/pages\/4384"}],"wp:attachment":[{"href":"https:\/\/app.taekwon-book.com\/en\/wp-json\/wp\/v2\/media?parent=4405"}],"wp:term":[{"taxonomy":"categorie-technique","embeddable":true,"href":"https:\/\/app.taekwon-book.com\/en\/wp-json\/wp\/v2\/categorie-technique?post=4405"},{"taxonomy":"ceinture","embeddable":true,"href":"https:\/\/app.taekwon-book.com\/en\/wp-json\/wp\/v2\/ceinture?post=4405"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}