{"id":334,"date":"2025-11-25T15:18:53","date_gmt":"2025-11-25T15:18:53","guid":{"rendered":"https:\/\/i-cte.org\/robot\/?p=334"},"modified":"2025-11-25T15:25:59","modified_gmt":"2025-11-25T15:25:59","slug":"people-describing-pictures","status":"publish","type":"post","link":"https:\/\/i-cte.org\/robot\/people-describing-pictures\/","title":{"rendered":"People &#8211; Describing Pictures"},"content":{"rendered":"\n<p>This Robot is used for practicing English at level A2. In order to interact with this Robot smoothly, please use a laptop, desktop, or iPad. Do not use Smartphones.<\/p>\n\n\n\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>People &#8211; Describing Pictures<\/title>\n    <style>\n        * { box-sizing: border-box; }\n\n        body {\n            font-family: Arial, sans-serif;\n            background-color: #f4f6f8;\n            margin: 0;\n            padding: 0;\n            display: flex;\n            flex-direction: column;\n            min-height: 100vh;\n        }\n\n        .menu {\n            width: 100%;\n            background-color: #28a745;\n            padding: 10px;\n            text-align: center;\n            box-shadow: 0 2px 5px rgba(0,0,0,0.1);\n            margin-bottom: 10px;\n            overflow-x: auto;\n            white-space: nowrap;\n        }\n        .menu a {\n            color: #fff;\n            text-decoration: none;\n            margin: 0 10px;\n            font-weight: bold;\n            font-size: 14px;\n            display: inline-block;\n        }\n        .menu a:hover,\n        .menu a:focus {\n            text-decoration: underline;\n            outline: 2px dashed #fff;\n            outline-offset: 4px;\n        }\n\n        .container {\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            padding: 10px;\n            flex: 1 0 auto;\n            width: 100%;\n        }\n\n        .image-chat-wrapper {\n            display: flex;\n            flex-direction: column;\n            width: 100%;\n            max-width: 900px;\n            flex: 1;\n        }\n\n        .image-container {\n            flex: 1;\n            text-align: center;\n            margin-bottom: 15px;\n        }\n        .image-container img {\n            width: 100%;\n            height: auto;\n            max-height: 320px;\n            object-fit: cover;\n            border-radius: 15px;\n            box-shadow: 0 0 8px rgba(0,0,0,0.1);\n        }\n        .sentence-text {\n            margin: 10px 0;\n            font-weight: bold;\n            color: #333;\n            font-size: 16px;\n        }\n        .word-box {\n            margin-top: 10px;\n            padding: 10px;\n            background-color: #fff;\n            border: 2px solid #28a745;\n            border-radius: 8px;\n            box-shadow: 0 0 8px rgba(0,0,0,0.1);\n            font-size: 15px;\n            font-weight: bold;\n            color: #28a745;\n            text-align: left;\n            max-height: 260px;\n            overflow-y: auto;\n        }\n\n        .chat-container {\n            background-color: #28a745;\n            border-radius: 15px;\n            box-shadow: 0 0 8px rgba(0,0,0,0.1);\n            border: 3px solid #007bff;\n            overflow: hidden;\n            display: flex;\n            flex-direction: column;\n            height: auto;\n            max-height: 520px;\n            flex: 1;\n        }\n        .chat-header {\n            background-color: #1e7e34;\n            color: #fff;\n            padding: 12px;\n            text-align: center;\n            border-bottom: 1px solid #155724;\n            position: relative;\n        }\n        .chat-header h2 {\n            margin: 0;\n            font-size: 18px;\n        }\n        .chat-header .loader {\n            position: absolute;\n            right: 20px;\n            top: 50%;\n            transform: translateY(-50%);\n            display: none;\n        }\n\n        .chat-messages {\n            padding: 12px;\n            overflow-y: auto;\n            flex: 1;\n            background-color: #fff;\n            max-height: 320px;\n        }\n        .message {\n            margin-bottom: 10px;\n            padding: 10px;\n            border-radius: 6px;\n            font-size: 14px;\n            line-height: 1.5;\n        }\n        .user-message {\n            background-color: #ffc107;\n            color: #fff;\n            text-align: right;\n        }\n        .bot-message {\n            background-color: #17a2b8;\n            color: #fff;\n            text-align: left;\n        }\n\n        .chat-input {\n            display: flex;\n            align-items: center;\n            background-color: #c3e6cb;\n            padding: 10px;\n            justify-content: space-between;\n            flex-wrap: wrap;\n        }\n        .send-button, .stop-button {\n            background-color: #17a2b8;\n            color: #fff;\n            font-weight: bold;\n            border: none;\n            border-radius: 5px;\n            padding: 12px 16px;\n            cursor: pointer;\n            transition: background-color 0.3s;\n            flex: 1;\n            margin: 6px;\n            max-width: 150px;\n            font-size: 16px;\n        }\n        .stop-button { background-color: #dc3545; }\n        .send-button:hover,\n        .stop-button:hover,\n        .send-button:focus,\n        .stop-button:focus {\n            background-color: #218838;\n            outline: none;\n        }\n\n        .voice-selection {\n            margin: 10px 0;\n            width: 100%;\n            max-width: 300px;\n        }\n        .voice-selection label {\n            display: block;\n            margin-bottom: 5px;\n            font-weight: bold;\n            color: #333;\n        }\n        .voice-selection select {\n            width: 100%;\n            padding: 8px;\n            border-radius: 5px;\n            border: 1px solid #ccc;\n            font-size: 14px;\n        }\n\n        .loader {\n            border: 4px solid #f3f3f3;\n            border-top: 4px solid #17a2b8;\n            border-radius: 50%;\n            width: 20px;\n            height: 20px;\n            animation: spin 2s linear infinite;\n            display: inline-block;\n            margin-left: 10px;\n        }\n        @keyframes spin {\n            0% { transform: rotate(0deg); }\n            100% { transform: rotate(360deg); }\n        }\n\n        @media (orientation: portrait) {\n            .image-chat-wrapper {\n                flex-direction: column;\n            }\n            .image-container, .chat-container {\n                width: 100%;\n                max-width: 100%;\n            }\n        }\n\n        @media (orientation: landscape) and (min-width: 600px) {\n            .image-chat-wrapper {\n                flex-direction: row;\n                justify-content: space-between;\n            }\n            .image-container, .chat-container {\n                width: 48%;\n                max-width: 48%;\n            }\n            .word-box { max-height: 220px; }\n            .chat-header h2 { font-size: 20px; }\n            .message { font-size: 15px; }\n            .send-button, .stop-button {\n                font-size: 18px;\n                padding: 14px 20px;\n                max-width: 200px;\n            }\n        }\n\n        @media (max-width: 768px) {\n            .chat-header h2 { font-size: 16px; }\n            .message { font-size: 14px; }\n            .send-button, .stop-button {\n                font-size: 16px;\n                padding: 12px 16px;\n                max-width: 150px;\n            }\n            .word-box { font-size: 15px; }\n            .voice-selection select { font-size: 14px; }\n        }\n\n        @media only screen and (max-device-width: 480px) and (-webkit-min-device-pixel-ratio: 2) {\n            .chat-header h2 { font-size: 18px; }\n            .send-button, .stop-button {\n                padding: 14px 18px;\n                font-size: 16px;\n            }\n            .message { font-size: 14px; }\n            .word-box { font-size: 16px; }\n        }\n\/* Container keeps wrapping *\/\n.chat-input {\n    display: flex;\n    align-items: center;\n    background-color: #c3e6cb;\n    padding: 10px;\n    flex-wrap: wrap;          \/* allow next line *\/\n    justify-content: center;  \/* center buttons *\/\n}\n\n\/* Voice selector takes the full first row *\/\n.chat-input .voice-selection {\n    width: 100%;\n    max-width: 100%;\n    margin-bottom: 10px;\n}\n\n\/* Start & Stop: same size, on one row *\/\n.send-button,\n.stop-button {\n    flex: 0 0 calc(50% - 12px);  \/* two equal buttons *\/\n    max-width: calc(50% - 12px);\n    margin: 6px;\n    font-size: 18px;\n    padding: 14px 0;\n}\n\n\/* Optional: on very small screens, make them full width *\/\n@media (max-width: 480px) {\n    .send-button,\n    .stop-button {\n        flex: 0 0 100%;\n        max-width: 100%;\n    }\n}\n\n\n    <\/style>\n<\/head>\n<body>\n    <!-- Adjust links to fit your site if needed -->\n    <div class=\"menu\">\n      <a href=\"https:\/\/i-cte.org\/robot\/people-conversation\/\">Conversation<\/a>\n      <a href=\"https:\/\/i-cte.org\/robot\/people-vocabulary\/\">Vocabulary<\/a>\n      <a href=\"https:\/\/i-cte.org\/robot\/people-real-life\/\">Real Life<\/a>\n      <a href=\"https:\/\/i-cte.org\/robot\/people-describing-pictures\/\">Describing Pictures<\/a>\n      <a href=\"https:\/\/i-cte.org\/robot\/people-reading-comprehension\/\">Reading<\/a>\n      <a href=\"https:\/\/i-cte.org\/robot\/people-listening\/\">Listening<\/a>\n    <\/div>\n\n    <div class=\"container\">\n        <div class=\"image-chat-wrapper\">\n            <div class=\"image-container\">\n                <!-- You can replace this with your own combined picture if you upload it -->\n                <img decoding=\"async\" src=\"https:\/\/i-cte.org\/robot\/wp-content\/uploads\/2025\/11\/Screenshot-2025-11-25-at-3.29.37-PM.png\" alt=\"Robot teacher asking you to describe people in the pictures\">\n                <div class=\"sentence-text\">\n                    Look at your pictures. Choose one person and describe their appearance and personality. I\u2019ll listen, give you feedback, and try to guess who it is!\n                <\/div>\n                <div id=\"word-box\" class=\"word-box\"><\/div>\n            <\/div>\n\n            <div class=\"chat-container\">\n                <div class=\"chat-header\">\n                    <h2>People &#8211; Describing Pictures<\/h2>\n                    <div class=\"loader\" id=\"synthesis-loader\"><\/div>\n                <\/div>\n                <div class=\"chat-messages\" id=\"chat-messages\"><\/div>\n                <div class=\"chat-input\">\n                    <div class=\"voice-selection\">\n                        <label for=\"voice-select\">Choose Voice (US \/ UK):<\/label>\n                        <select id=\"voice-select\" aria-label=\"Select Voice\">\n                            <option value=\"\">Loading voices&#8230;<\/option>\n                        <\/select>\n                    <\/div>\n                    <button id=\"start-btn\" class=\"send-button\" aria-label=\"Start Lesson\">Start<\/button>\n                    <button id=\"stop-btn\" class=\"stop-button\" aria-label=\"Stop Lesson\">Stop<\/button>\n                <\/div>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <div class=\"menu\">\n      <a href=\"https:\/\/i-cte.org\/robot\/people-conversation\/\">Conversation<\/a>\n      <a href=\"https:\/\/i-cte.org\/robot\/people-vocabulary\/\">Vocabulary<\/a>\n      <a href=\"https:\/\/i-cte.org\/robot\/people-real-life\/\">Real Life<\/a>\n      <a href=\"https:\/\/i-cte.org\/robot\/people-describing-pictures\/\">Describing Pictures<\/a>\n      <a href=\"https:\/\/i-cte.org\/robot\/people-reading-comprehension\/\">Reading<\/a>\n      <a href=\"https:\/\/i-cte.org\/robot\/people-listening\/\">Listening<\/a>\n    <\/div>\n\n<script>\n    \/\/ ROUNDS: 3 picture-description rounds + 1 challenge round\n    const rounds = [\n        {\n            label: \"Round 1 \u2013 Age & Height\",\n            type: \"picture\",\n            focus: \"age and height\",\n            instructions:\n                \"Choose one person from your pictures. Don\u2019t say where they are. Describe their age and height. Then add at least one more detail.\",\n            example:\n                \"This person is young. He\u2019s fairly short and quite thin. He has short dark hair.\",\n            requiredCategories: [\"age\", \"height\"]\n        },\n        {\n            label: \"Round 2 \u2013 Hair & Face\",\n            type: \"picture\",\n            focus: \"hair and face\",\n            instructions:\n                \"Choose another person. Describe their hair and face. You can talk about hair length, color, style, beard, mustache, or glasses.\",\n            example:\n                \"She\u2019s middle-aged. She has long dark hair and big glasses. She\u2019s fairly tall.\",\n            requiredCategories: [\"hair\"]\n        },\n        {\n            label: \"Round 3 \u2013 Personality & Action\",\n            type: \"picture\",\n            focus: \"personality and what they are doing\",\n            instructions:\n                \"Choose a third person. Describe what kind of person he or she is and what they are doing in the picture.\",\n            example:\n                \"He\u2019s friendly and very funny. He\u2019s playing the guitar and singing to his friends.\",\n            requiredCategories: [\"personality\", \"action\"]\n        },\n        {\n            label: \"Challenge \u2013 Someone You Know\",\n            type: \"challenge\",\n            focus: \"a real person you know well\",\n            instructions:\n                \"Now think of a real person you know well, like a friend or family member. Describe their appearance and character. Explain why you think they are like that.\",\n            example:\n                \"My best friend is fairly short and slim. She has long straight brown hair. She\u2019s very hard-working and kind because she always helps other people.\",\n            requiredCategories: [\"age\", \"height\", \"hair\", \"personality\"]\n        }\n    ];\n\n    \/\/ Useful vocabulary categories\n    const ageWords = [\n        \"young\",\"old\",\"middle-aged\",\"middle aged\",\"teenager\",\"teenage\",\"child\",\"kid\",\"in his twenties\",\"in her twenties\"\n    ];\n    const heightWords = [\n        \"tall\",\"short\",\"medium height\",\"medium-height\",\"fairly tall\",\"fairly short\"\n    ];\n    const buildWords = [\n        \"thin\",\"slim\",\"skinny\",\"heavy\",\"overweight\",\"quite thin\",\"fairly thin\"\n    ];\n    const hairWords = [\n        \"hair\",\"short hair\",\"long hair\",\"curly\",\"straight\",\"wavy\",\n        \"blond\",\"blonde\",\"brown hair\",\"black hair\",\"red hair\",\"bald\",\n        \"beard\",\"mustache\",\"moustache\"\n    ];\n    const personalityWords = [\n        \"shy\",\"friendly\",\"polite\",\"hard-working\",\"hardworking\",\"lazy\",\n        \"funny\",\"serious\",\"interesting\",\"kind\",\"helpful\",\"quiet\",\"talkative\"\n    ];\n    const actionWords = [\n        \"playing\",\"working\",\"studying\",\"reading\",\"sleeping\",\"talking\",\n        \"cooking\",\"drawing\",\"singing\",\"playing the guitar\",\"watching\",\n        \"listening\",\"giving\",\"helping\",\"cleaning\",\"writing\"\n    ];\n\n    \/\/ Picture-based guessing \u2013 approximate tags for the people in the big picture\n    const pictureCharacters = [\n        {\n            name: \"the grandmother with the little boy and the shopping bag\",\n            tags: [\"grandmother\",\"grandma\",\"old woman\",\"shopping\",\"bag\",\"boy\",\"little boy\",\"drawing\",\"painting\"]\n        },\n        {\n            name: \"the boy in the cap feeding the dog\",\n            tags: [\"dog\",\"puppy\",\"cap\",\"hat\",\"kneeling\",\"feeding\",\"young boy\",\"orange\"]\n        },\n        {\n            name: \"the hair stylist cutting the man\u2019s hair\",\n            tags: [\"haircut\",\"hair cut\",\"hairdresser\",\"hair stylist\",\"barber\",\"salon\",\"chair\",\"mirror\"]\n        },\n        {\n            name: \"the group of people standing and talking at the party\",\n            tags: [\"party\",\"group\",\"crowd\",\"standing\",\"talking\",\"friends\",\"people\"]\n        },\n        {\n            name: \"the man at the window with his family saying goodbye\",\n            tags: [\"bus\",\"train\",\"goodbye\",\"waving\",\"suitcase\",\"luggage\",\"window\",\"leaving\"]\n        },\n        {\n            name: \"the woman working at the desk and the tired boy\",\n            tags: [\"desk\",\"homework\",\"computer\",\"papers\",\"books\",\"sleepy\",\"tired\",\"student\",\"table\"]\n        },\n        {\n            name: \"the man playing the guitar for his friends\",\n            tags: [\"guitar\",\"music\",\"playing guitar\",\"singing\",\"outside\",\"park\",\"tree\",\"friends\",\"camping\"]\n        },\n        {\n            name: \"the girl giving a present to the man with the newspaper\",\n            tags: [\"present\",\"gift\",\"box\",\"newspaper\",\"sofa\",\"couch\",\"father\",\"armchair\"]\n        },\n        {\n            name: \"the man having a meeting with his boss in the office\",\n            tags: [\"office\",\"boss\",\"interview\",\"suit\",\"desk\",\"chair\",\"plant\",\"meeting\",\"manager\"]\n        }\n    ];\n\n    const stopKeywords = [\"goodbye\",\"good bye\",\"bye\",\"bye-bye\",\"byebye\"];\n\n    let currentRoundIndex = 0;\n    let recognition;\n    let isLessonActive = false;\n    let speechRecognitionActive = false;\n\n    const speechSynthesisSupported = 'speechSynthesis' in window;\n    const speechRecognitionSupported = window.SpeechRecognition || window.webkitSpeechRecognition;\n\n    let voices = [];\n    let selectedVoice = null;\n\n    const desiredVoiceNames = [\n        \"Google US English\",\n        \"Google US English Female\",\n        \"Google UK English Male\",\n        \"Google UK English Female\"\n    ];\n\n    function initialize() {\n        loadVoices();\n\n        \/\/ Fill word box with useful language at the beginning\n        fillInitialWordBox();\n\n        if (speechSynthesisSupported) {\n            setTimeout(greetUser, 500);\n        } else {\n            appendMessage(\"Sorry, your browser does not support speech synthesis.\", \"bot\");\n        }\n\n        if (!speechRecognitionSupported) {\n            appendMessage(\"Sorry, your browser does not support speech recognition.\", \"bot\");\n        }\n\n        document.getElementById(\"start-btn\").onclick = function() {\n            if (isLessonActive) return;\n\n            if (!speechSynthesisSupported) {\n                appendMessage(\"Speech synthesis is not supported in your browser.\", \"bot\");\n                return;\n            }\n            if (!speechRecognitionSupported) {\n                appendMessage(\"Speech recognition is not supported in your browser.\", \"bot\");\n                return;\n            }\n\n            isLessonActive = true;\n            currentRoundIndex = 0;\n            startRound();\n        };\n\n        document.getElementById(\"stop-btn\").onclick = function() {\n            if (recognition && speechRecognitionActive) {\n                recognition.stop();\n                speechRecognitionActive = false;\n            }\n            endLesson(true);\n        };\n    }\n\n    function loadVoices() {\n        voices = window.speechSynthesis.getVoices();\n        if (voices.length === 0) {\n            window.speechSynthesis.onvoiceschanged = () => {\n                voices = window.speechSynthesis.getVoices();\n                populateVoiceList();\n            };\n        } else {\n            populateVoiceList();\n        }\n    }\n\n    function populateVoiceList() {\n        const voiceSelect = document.getElementById(\"voice-select\");\n        voiceSelect.innerHTML = \"\";\n\n        const filteredVoices = voices.filter(v => desiredVoiceNames.includes(v.name));\n\n        if (filteredVoices.length === 0) {\n            const option = document.createElement(\"option\");\n            option.value = \"\";\n            option.textContent = \"No desired voices available\";\n            voiceSelect.appendChild(option);\n            voiceSelect.disabled = true;\n            return;\n        }\n\n        filteredVoices.forEach((voice, index) => {\n            const option = document.createElement(\"option\");\n            option.value = index;\n            option.textContent = `${voice.name} (${voice.lang})`;\n            voiceSelect.appendChild(option);\n        });\n\n        voiceSelect.selectedIndex = 0;\n        selectedVoice = filteredVoices[0];\n\n        voiceSelect.onchange = () => {\n            const selectedIndex = voiceSelect.value;\n            selectedVoice = filteredVoices[selectedIndex];\n        };\n    }\n\n    function greetUser() {\n        const greeting =\n            \"Hello! In this lesson you will describe people in pictures. Use phrases like: This person is young. He\u2019s fairly short. He has short blond hair and a beard. I\u2019ll listen, give you feedback, and try to guess who you are describing. Click Start when you are ready.\";\n        appendMessage(greeting, \"bot\");\n        showSynthesisLoader(true);\n        sayText(greeting, \"en-US\").then(() => showSynthesisLoader(false));\n    }\n\n    function fillInitialWordBox() {\n        clearWordBox();\n        addSentenceToBox(\"Useful language for describing people:\");\n        addSentenceToBox(\"Age: young, middle-aged, old\");\n        addSentenceToBox(\"Height & build: fairly tall, fairly short, medium height, thin, slim\");\n        addSentenceToBox(\"Hair & face: short blond hair, long dark hair, curly hair, a beard, a mustache, glasses\");\n        addSentenceToBox(\"Personality: shy, friendly, polite, hard-working, funny, serious, kind\");\n        addSentenceToBox(\"Sentence patterns:\");\n        addSentenceToBox(\"\u2022 This person is young \/ middle-aged \/ old.\");\n        addSentenceToBox(\"\u2022 He\u2019s fairly short. She\u2019s fairly tall.\");\n        addSentenceToBox(\"\u2022 He has short blond hair and a beard.\");\n        addSentenceToBox(\"\u2022 She has long dark hair and glasses.\");\n        addSentenceToBox(\"\u2022 I think he\u2019s shy because \u2026\");\n        addSentenceToBox(\"\u2022 I think she\u2019s friendly because \u2026\");\n    }\n\n    function startRound() {\n        if (!isLessonActive) return;\n\n        if (currentRoundIndex >= rounds.length) {\n            endLesson(false);\n            return;\n        }\n\n        const round = rounds[currentRoundIndex];\n\n        \/\/ Update word box for this round\n        clearWordBox();\n        addSentenceToBox(round.label);\n        addSentenceToBox(\"Focus: \" + round.focus);\n        addSentenceToBox(\"Instructions:\");\n        addSentenceToBox(round.instructions);\n        addSentenceToBox(\"Example:\");\n        addSentenceToBox(round.example);\n\n        appendMessage(round.label + \" \u2013 \" + round.focus, \"bot\");\n        appendMessage(round.instructions, \"bot\");\n\n        showSynthesisLoader(true);\n        const speakText = `${round.label}. ${round.instructions} Here is an example: ${round.example}`;\n        sayText(speakText, \"en-US\").then(() => {\n            showSynthesisLoader(false);\n            listenForDescription(round);\n        });\n    }\n\n    function listenForDescription(round) {\n        if (!speechRecognitionSupported) return;\n\n        recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();\n        recognition.lang = \"en-US\";\n        recognition.interimResults = false;\n        recognition.maxAlternatives = 1;\n\n        showRecognitionLoader(true);\n        appendMessage(\"Listening...\", \"bot\");\n        speechRecognitionActive = true;\n        recognition.start();\n\n        recognition.onresult = function(event) {\n            if (!speechRecognitionActive) return;\n\n            const userInput = event.results[0][0].transcript.trim();\n            const formattedInput = normalizeAndFormatUserInput(userInput);\n\n            appendMessage(formattedInput, \"user\");\n            addSentenceToBox(\"Your description: \" + formattedInput);\n\n            showRecognitionLoader(false);\n            speechRecognitionActive = false;\n\n            if (checkStopKeywords(formattedInput.toLowerCase())) {\n                endLesson(true);\n                return;\n            }\n\n            const feedback = buildFeedback(formattedInput, round);\n            appendMessage(feedback, \"bot\");\n            showSynthesisLoader(true);\n            sayText(feedback, \"en-US\").then(() => {\n                showSynthesisLoader(false);\n                currentRoundIndex++;\n                setTimeout(startRound, 2600);\n            });\n        };\n\n        recognition.onspeechend = function() {\n            if (speechRecognitionActive) {\n                recognition.stop();\n                speechRecognitionActive = false;\n                showRecognitionLoader(false);\n            }\n        };\n\n        recognition.onerror = function(event) {\n            showRecognitionLoader(false);\n            speechRecognitionActive = false;\n            let msg = \"Error: \" + event.error;\n\n            if (event.error === \"not-allowed\") {\n                msg = \"Microphone access was not allowed. Please allow microphone access in your browser settings and try again.\";\n            }\n\n            appendMessage(msg, \"bot\");\n\n            const retryMessage = \"Let\u2019s try that description again.\";\n            appendMessage(retryMessage, \"bot\");\n            showSynthesisLoader(true);\n            sayText(retryMessage, \"en-US\").then(() => {\n                showSynthesisLoader(false);\n                setTimeout(() => listenForDescription(round), 2500);\n            });\n        };\n    }\n\n    function buildFeedback(userInput, round) {\n        const lower = userInput.toLowerCase();\n        const feedbackParts = [];\n\n        \/\/ Grammar hints\n        const grammarHint = checkGrammar(lower);\n        if (grammarHint) {\n            feedbackParts.push(grammarHint);\n        }\n\n        \/\/ Category coverage\n        const categoriesUsed = detectCategories(lower);\n        const missingRequired = round.requiredCategories.filter(cat => !categoriesUsed[cat]);\n\n        if (missingRequired.length === 0) {\n            feedbackParts.push(\"Nice! You used the key ideas for this round.\");\n        } else {\n            missingRequired.forEach(cat => {\n                if (cat === \"age\") {\n                    feedbackParts.push(\"Try to mention the person\u2019s age, for example: This person is young or She\u2019s middle-aged.\");\n                } else if (cat === \"height\") {\n                    feedbackParts.push(\"Add something about height, such as fairly tall, fairly short, or medium height.\");\n                } else if (cat === \"hair\") {\n                    feedbackParts.push(\"Include details about hair or face, like short blond hair, long dark hair, a beard, or glasses.\");\n                } else if (cat === \"personality\") {\n                    feedbackParts.push(\"Say something about personality, for example: He\u2019s shy, she\u2019s friendly, or he\u2019s hard-working.\");\n                } else if (cat === \"action\") {\n                    feedbackParts.push(\"Tell me what the person is doing in the picture, using the present continuous, for example: He\u2019s playing the guitar.\");\n                }\n            });\n        }\n\n        \/\/ Extra praise if many categories\n        const categoryCount = Object.values(categoriesUsed).filter(v => v).length;\n        if (categoryCount >= 3 && !grammarHint) {\n            feedbackParts.push(\"Great description! You used several details and your grammar was clear.\");\n        }\n\n        \/\/ Guess which picture (only for picture rounds)\n        if (round.type === \"picture\") {\n            const guess = guessPicture(lower);\n            if (guess) {\n                feedbackParts.push(`I think you are describing ${guess}. Am I right?`);\n            } else {\n                feedbackParts.push(\"I\u2019m not completely sure which person it is, but your description helps me imagine them.\");\n            }\n        } else {\n            feedbackParts.push(\"Thank you for describing a real person. That\u2019s good practice for real-life conversations.\");\n        }\n\n        return feedbackParts.join(\" \");\n    }\n\n    function detectCategories(text) {\n        const used = {\n            age: false,\n            height: false,\n            build: false,\n            hair: false,\n            personality: false,\n            action: false\n        };\n\n        for (const w of ageWords) if (text.includes(w)) used.age = true;\n        for (const w of heightWords) if (text.includes(w)) used.height = true;\n        for (const w of buildWords) if (text.includes(w)) used.build = true;\n        for (const w of hairWords) if (text.includes(w)) used.hair = true;\n        for (const w of personalityWords) if (text.includes(w)) used.personality = true;\n        for (const w of actionWords) if (text.includes(w)) used.action = true;\n\n        return used;\n    }\n\n    function guessPicture(text) {\n        let best = null;\n        let bestScore = 0;\n\n        pictureCharacters.forEach(char => {\n            let score = 0;\n            char.tags.forEach(tag => {\n                if (text.includes(tag)) score++;\n            });\n            if (score > bestScore) {\n                bestScore = score;\n                best = char.name;\n            }\n        });\n\n        if (bestScore >= 2) {\n            return best;\n        }\n        return null;\n    }\n\n    \/\/ Simple grammar support for describing people\n    function checkGrammar(lower) {\n        if (lower.includes(\"he have \") || lower.includes(\"he has got got\")) {\n            return 'Remember: \"He has ...\" not \"He have ...\". For example, \"He has short blond hair and a beard.\"';\n        }\n        if (lower.includes(\"she have \")) {\n            return 'Remember: \"She has ...\" not \"She have ...\". For example, \"She has long dark hair.\"';\n        }\n        if (lower.includes(\"he are \") || lower.includes(\"she are \")) {\n            return 'Use \"is\" with he and she: \"He is fairly short.\" \/ \"She is very friendly.\"';\n        }\n        if (lower.includes(\"he have a beard and he has a beard\")) {\n            return 'You can make it shorter: \"He has a beard.\" You don\u2019t need to repeat it.';\n        }\n        if (lower.includes(\"he is look\") || lower.includes(\"she is look\")) {\n            return 'Say \"He looks ...\" or \"He is looking ...\", for example, \"He looks shy\" or \"He is looking tired.\"';\n        }\n        return null;\n    }\n\n    function checkStopKeywords(userInput) {\n        const pattern = new RegExp(\"\\\\b(\" + stopKeywords.join(\"|\") + \")\\\\b\", \"i\");\n        return pattern.test(userInput);\n    }\n\n    function normalizeAndFormatUserInput(text) {\n        let normalized = text.replace(\/\\bi\\b\/g, \"I\");\n        return formatSentence(normalized);\n    }\n\n    function formatSentence(text) {\n        if (!text) return text;\n        text = text.charAt(0).toUpperCase() + text.slice(1);\n\n        const questionWords = [\n            \"who\",\"what\",\"where\",\"when\",\"why\",\"how\",\n            \"is\",\"are\",\"do\",\"does\",\"can\",\"should\",\"will\",\"would\",\"did\"\n        ];\n        const firstWord = text.split(\" \")[0].toLowerCase();\n\n        if (questionWords.includes(firstWord) && !text.endsWith(\"?\")) {\n            text += \"?\";\n        } else if (!text.endsWith(\".\") && !text.endsWith(\"?\")) {\n            text += \".\";\n        }\n\n        return text;\n    }\n\n    function addSentenceToBox(sentence) {\n        const box = document.getElementById(\"word-box\");\n        const div = document.createElement(\"div\");\n        div.innerText = sentence;\n        box.appendChild(div);\n        box.scrollTop = box.scrollHeight;\n    }\n\n    function clearWordBox() {\n        document.getElementById(\"word-box\").innerHTML = \"\";\n    }\n\n    function endLesson(userStopped = false) {\n        if (speechSynthesisSupported) {\n            window.speechSynthesis.cancel();\n        }\n        if (recognition && speechRecognitionActive) {\n            recognition.stop();\n            speechRecognitionActive = false;\n        }\n\n        isLessonActive = false;\n\n        let message;\n        if (userStopped) {\n            message = \"Thank you for describing people today. Goodbye!\";\n        } else {\n            message = \"Well done! You finished all the rounds. Keep practicing these description sentences with your friends or classmates.\";\n        }\n        appendMessage(message, \"bot\");\n        showSynthesisLoader(true);\n        sayText(message, \"en-US\").then(() => showSynthesisLoader(false));\n    }\n\n    function sayText(text, lang) {\n        return new Promise(resolve => {\n            if (!speechSynthesisSupported) {\n                resolve();\n                return;\n            }\n            const utterance = new SpeechSynthesisUtterance(text);\n            utterance.lang = lang;\n            utterance.rate = 1.0;\n            utterance.pitch = 1.0;\n\n            if (selectedVoice) {\n                utterance.voice = selectedVoice;\n            }\n\n            utterance.onend = () => resolve();\n            utterance.onerror = () => resolve();\n\n            window.speechSynthesis.speak(utterance);\n        });\n    }\n\n    function appendMessage(text, sender) {\n        const container = document.getElementById(\"chat-messages\");\n        const messageElement = document.createElement(\"div\");\n        messageElement.classList.add(\"message\");\n        messageElement.classList.add(sender === \"bot\" ? \"bot-message\" : \"user-message\");\n        messageElement.innerText = text;\n        container.appendChild(messageElement);\n        container.scrollTop = container.scrollHeight;\n    }\n\n    function showSynthesisLoader(show) {\n        const loader = document.getElementById(\"synthesis-loader\");\n        loader.style.display = show ? \"inline-block\" : \"none\";\n        if (show) {\n            loader.style.borderTop = \"4px solid #17a2b8\";\n        }\n    }\n\n    function showRecognitionLoader(show) {\n        const loader = document.getElementById(\"synthesis-loader\");\n        if (show) {\n            loader.style.display = \"inline-block\";\n            loader.style.borderTop = \"4px solid #ffc107\";\n        } else {\n            loader.style.display = \"none\";\n        }\n    }\n\n    window.onload = initialize;\n<\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>This Robot is used for practicing English at level A2. In order to interact with this Robot smoothly, please use<\/p>\n","protected":false},"author":1,"featured_media":101,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"colormag_page_layout":"default_layout","footnotes":""},"categories":[18,5],"tags":[],"class_list":["post-334","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-a2","category-chatbot"],"_links":{"self":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/334","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/comments?post=334"}],"version-history":[{"count":3,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/334\/revisions"}],"predecessor-version":[{"id":342,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/posts\/334\/revisions\/342"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media\/101"}],"wp:attachment":[{"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/media?parent=334"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/categories?post=334"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/i-cte.org\/robot\/wp-json\/wp\/v2\/tags?post=334"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}