{"id":204,"date":"2026-06-04T20:23:56","date_gmt":"2026-06-05T00:23:56","guid":{"rendered":"https:\/\/wp.stgeorges.bc.ca\/akbarj\/?p=204"},"modified":"2026-06-04T20:23:56","modified_gmt":"2026-06-05T00:23:56","slug":"bci-flappy-bird-final-report","status":"publish","type":"post","link":"https:\/\/wp.stgeorges.bc.ca\/akbarj\/2026\/06\/04\/bci-flappy-bird-final-report\/","title":{"rendered":"BCI Flappy Bird &#8211; Final Report"},"content":{"rendered":"\n<p class=\"is-style-text-subtitle is-style-text-subtitle--1 wp-block-paragraph\">Project Goals<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What problem did you set out to solve?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every day, some people wake up unable to feel anything but their head. To eat breakfast, they are fed by a caretaker because their hands are paralyzed. Around 15.4 million people deal with Spinal Cord Injuries(SCIs) in the world. This is such a significant problem because paraplegic and tetraplegic people have much worse quality of their lives. They have to deal with all their daily activities without being able to move so much of their body. Paraplegics can&#8217;t move their legs and tetraplegics can&#8217;t move their arms or legs, making them almost immobile. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is a very large problem. To narrow the scope, I decided to prove that the brain&#8217;s signals are still there even if they are not able to send anything down to the lower parts of the body. If we can prove they are still present in the body, then later on the technology to artificially move those body parts can be made. Rather than replicating expensive BCI systems, this project looks at: What signals does that body still produce and how can it be used?<\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--2 wp-block-paragraph\">Project Constraints<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Who are your users?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Because this is such a simple scope of the project, I think the primary user is myself in order to understand the field better. This project could be used by bored patients who wanted to play Flappy Bird, however, it is not at a level to improve their quality of life in any real way.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eventually when this projects gets to a usable level, I will be catering to SCI patients. Typically, these are males with an average age of injury of 43 years old. According to a study on BCIs after SCI, a survey was done on veterans with SCI, and they all wanted restoration of bladder and bowel control, walking, and arm\/hand functionality. Overall, they just wanted independent operation. Although BCI systems for rehabilitation already exist, they face two major problems. The first is that they are still in the experimental phase and are not ready for clinical application. The second is that non-invasive options struggle with consistent signals and are bulky and uncomfortable. This gap is where my project will fit in &#8211; a non-invasive option that is convenient to those users.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What is the time frame of the project?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The project ran from early February to early May, making it about 3 months.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What other constraints have you identified?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One of the biggest constraints for me for this project was hardware. First of all, all headsets that would work for this kind of project were very expensive. In terms of budget, we ended up spending $350 CAD on the headset and an additional $15 for access to the platform &#8211; MindMonitor &#8211; that streamed the headset&#8217;s signals wirelessly to my laptop. This headset &#8211; the Muse 2 &#8211; was one of the cheapest I could find to work with this kind of project. I also had to deal with the problem that the electrodes were not placed on the motor cortex, the part of the brain associated with movement. On top of that, I had never done any projects in this field &#8211; one more reason why it was such a simple project &#8211; and this constraint made me have to learn how to deal with coding in this environment.<\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--3 wp-block-paragraph\">Project Expectations<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What is the scope of your problem?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When I was making the project, I knew it should focus on reading the brain signals from the Muse 2 and use them for something. I did not intend to work with actual SCI patients or make a medical device because I knew I was still learning how the field works. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>How will you identify or define success?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If I could get an input from the brain and use it, I would call that success.<\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--4 wp-block-paragraph\">Design Choices<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What is your solution?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I built a system for people to play Flappy Bird with their mind. The framework starts with a Muse 2 headset that receives signals from the TP9, TP10, AF7, and AF8 electrodes. Those signals are sent to my phone using Bluetooth, then streamed to my laptop using the Mind Monitor app. Once the signals are on the computer, the code would watch for the signal&#8217;s amplitude to pass a certain threshold, causing a jump in the Flappy Bird game. Every time the user triggered a spike, the bird jumped.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">All of these choices were intentional decisions. The Muse 2 was one of the cheapest headsets that was in line with what I was trying to do. Because I decided on the Muse 2, I used Mind Monitor because their app is built for the Muse ecosystem. A simple amplitude threshold detector was used over a more complex algorithm because for this use case, that&#8217;s all that is required. All the game needs is when it should jump and when it shouldn&#8217;t, and a complex classifier would just be unneeded complexity. Flappy Bird was used because it is just a binary jump or no jump that fits perfectly with the vision.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What does it look like?<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"759\" height=\"999\" src=\"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/image-1-edited.png\" alt=\"\" class=\"wp-image-208\" srcset=\"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/image-1-edited.png 759w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/image-1-edited-228x300.png 228w\" sizes=\"auto, (max-width: 759px) 100vw, 759px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"825\" src=\"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/image-2-1024x825.png\" alt=\"\" class=\"wp-image-209\" srcset=\"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/image-2-1024x825.png 1024w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/image-2-300x242.png 300w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/image-2-768x619.png 768w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/image-2.png 1440w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"473\" height=\"1024\" src=\"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-12.21.03-PM-473x1024.png\" alt=\"\" class=\"wp-image-214\" srcset=\"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-12.21.03-PM-473x1024.png 473w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-12.21.03-PM-139x300.png 139w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-12.21.03-PM-768x1662.png 768w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-12.21.03-PM-710x1536.png 710w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-12.21.03-PM-947x2048.png 947w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-12.21.03-PM-scaled.png 1183w\" sizes=\"auto, (max-width: 473px) 100vw, 473px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--5 wp-block-paragraph\">Project Planning<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What are the steps to get to your goal?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, I started with making sure the framework worked. I wanted to see if the signals could go from the headset to my computer. I made a file that I later built off of to receive the streaming data and just log it onto my computer. Once that was successful, I used that data to make a threshold reader that would trigger every time the signal passed the threshold. I then made the game with the help of Claude Code. I decided to use Claude Code because I saw the game as a way to test my project rather than the actual project itself. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What feedback have you received? From where? How has your design evolved?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I didn&#8217;t receive much feedback on my project because I only showcased it at the project presentations and no sooner. Looking back, I think this is a mistake because some feedback might have helped me evolve my design. <\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--6 wp-block-paragraph\">Bill of Materials and Cost Analysis<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>Muse 2 Headset<\/td><td>$350<\/td><\/tr><tr><td>Mind Monitor app<\/td><td>$15<\/td><\/tr><tr><td>Python and Libraries<\/td><td>Free<\/td><\/tr><tr><td>Claude Code<\/td><td>Free access<\/td><\/tr><tr><td><strong>Total<\/strong><\/td><td><strong>$365<\/strong><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">$365 seems like a very high price for what I ended up building. The reason is that BCIs are such a new field, and breaking into it requires a high upfront cost. However, the headset I ended up purchasing is not even very powerful for the types of applications I am interested in, but those headsets go into the thousands. <\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--7 wp-block-paragraph\">Testing and Validation<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Why do you think that your solution will be successful?\u00a0 What evidence do you have?\u00a0 If you tested the prototype, how did you do this and what did the test look like?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once I had everything ready, I needed to test it and play the game. I tried different ways of controlling the signal &#8211; jaw clenching, eyebrow raising, and blinking &#8211; to see which were the most powerful and most consistent. After trying to trigger a jump with those different EMG signals I understood which were better and worse.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td>Type<\/td><td>Pros<\/td><td>Cons<\/td><\/tr><tr><td>Clench Jaw<\/td><td>Consistent, strong signal<\/td><td>High effort required<\/td><\/tr><tr><td>Blink <\/td><td>Good signal, low effort<\/td><td>Inconsistent signal<\/td><\/tr><tr><td>Raise Eyebrows<\/td><td>Consistent, good signal, low effort<\/td><td>Looks funny<br>(No real cons)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">All of them worked to play Flappy Bird. I was able to get good scores with all of these methods. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Although these signals are not actually EEG signals, they would still be useful in helping someone with a SCI. They are either paralyzed below the neck or below the torso, but their face is still able to move in both cases. Knowing when a blink, a clench, or an eyebrow raise is performed could lead to a simple control mechanism for an SCI patient. Even though direct mind control seems ideal, it is hard to train someone who hasn&#8217;t engaged their motor cortex in so long to imagine movements like jumping. For this reason, we can use actions they are already familiar with to allow them to use technology that will improve their quality of life.<\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--8 wp-block-paragraph\">Sustainability \/ Code of Ethics<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What ethical implications exist in your problem, solution, and process?<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Patients with SCIs are very vulnerable with their conditions, and if they are in a situation where they depend on BCI technology that fails, they will be greatly affected. That failure may cause serious problems in their lives and they will probably need to be back in a hospital relying on the old hospital workers they used to have. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In terms of privacy, although my specific project doesn&#8217;t present any serious concerns, this technology could be taken advantage of in the future by interpreting the raw data from the brain. The level of damage this could cause depends on the sophistication of the headset and of the interpretation algorithm.<\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--9 wp-block-paragraph\">Conclusions and Recommendations<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I think that I succeeded because I was able to use an input that came from the brain to do something &#8211; jump in Flappy Bird. This matters because now I&#8217;ve worked with BCIs and this opens many doors for later on. In the future, I hope to continue my work with BCIs. I hope to get access to a more powerful one so that I can build impactful projects. If someone wanted to continue this work, they should focus on getting access to better hardware. As I worked on this project, I noticed that the limitations of this headset limited what I was able to build. I think that if I had a more powerful headset with electrodes on the motor cortex, I would&#8217;ve been able to build a more complex and useful project.<\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--10 wp-block-paragraph\">References <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">World Health Organization. (2024, April 16). <em>Spinal cord injury<\/em>. <a href=\"https:\/\/www.who.int\/news-room\/fact-sheets\/detail\/spinal-cord-injury\">https:\/\/www.who.int\/news-room\/fact-sheets\/detail\/spinal-cord-injury<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Collinger, J. L., Boninger, M. L., Bruns, T. M., Curley, K., Wang, W., &amp; Weber, D. J. (2013). Functional priorities, assistive technology, and brain-computer interfaces after spinal cord injury. <em>Journal of Rehabilitation Research and Development, 50<\/em>(2), 145\u2013160. <a href=\"https:\/\/www.ncbi.nlm.nih.gov\/pmc\/articles\/PMC3684986\/\">https:\/\/www.ncbi.nlm.nih.gov\/pmc\/articles\/PMC3684986\/<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Jiang, L., Li, F., Tang, Y., &amp; Liu, X. (2023). Brain\u2013computer interface: trend, challenges, and threats. <em>Frontiers in Computational Neuroscience.<\/em> <a href=\"https:\/\/pmc.ncbi.nlm.nih.gov\/articles\/PMC10403483\/\">https:\/\/pmc.ncbi.nlm.nih.gov\/articles\/PMC10403483\/<\/a><\/p>\n\n\n\n<p class=\"is-style-text-subtitle is-style-text-subtitle--11 wp-block-paragraph\">Appendices<\/p>\n\n\n\n<p class=\"is-style-default wp-block-paragraph\">Appendix A \u2014 Final Project Code<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">oscreceiver.py<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from pythonosc import dispatcher, osc_server\nimport argparse\nimport time\n\nPORT = 5000\n\n\ndef handle_eeg(address, *args):\n    labels = &#091;\"TP9\", \"AF7\", \"AF8\", \"TP10\", \"AUX\"]\n    values = \"  \".join(f\"{l}={v:+8.2f}\" for l, v in zip(labels, args))\n    print(f\"&#091;EEG] {values}\")\n\n\ndef handle_any(address, *args):\n    print(f\"&#091;OSC] {address}  {list(args)}\")\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Muse 2 OSC receiver\")\n    parser.add_argument(\"--port\", type=int, default=PORT)\n    parser.add_argument(\"--eeg-only\", action=\"store_true\",\n                        help=\"Print only \/muse\/eeg messages\")\n    args = parser.parse_args()\n\n    d = dispatcher.Dispatcher()\n    d.map(\"\/muse\/eeg\", handle_eeg)\n    if not args.eeg_only:\n        d.set_default_handler(handle_any)\n\n    server = osc_server.ThreadingOSCUDPServer((\"0.0.0.0\", args.port), d)\n    print(f\"Listening for OSC on UDP port {args.port} \u2026  (Ctrl-C to stop)\")\n    print(\"Expected channel order: TP9  AF7  AF8  TP10  AUX\\n\")\n    try:\n        server.serve_forever()\n    except KeyboardInterrupt:\n        print(\"\\nStopped.\")\n\n\nif __name__ == \"__main__\":\n    main()\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">jaw_detector.py<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>from pythonosc import dispatcher, osc_server\nfrom collections import deque\nimport argparse\nimport math\nimport time\n\nSAMPLE_RATE_HZ = 256\nDEFAULT_WINDOW_SEC = 0.25\nDEFAULT_THRESHOLD_UV = 150.0\nDEFAULT_DEBOUNCE_SEC = 0.35\n\n\nclass JawClenchDetector:\n    def __init__(\n        self,\n        threshold_uv: float = DEFAULT_THRESHOLD_UV,\n        window_sec: float = DEFAULT_WINDOW_SEC,\n        debounce_sec: float = DEFAULT_DEBOUNCE_SEC,\n        on_clench=None,\n    ):\n        self.threshold = threshold_uv\n        self.debounce = debounce_sec\n        self.on_clench = on_clench\n        window_samples = max(1, int(window_sec * SAMPLE_RATE_HZ))\n        self.tp9 = deque(maxlen=window_samples)\n        self.tp10 = deque(maxlen=window_samples)\n        self.last_fire_time = 0.0\n        self.last_amplitude = 0.0\n\n    def feed(self, tp9: float, tp10: float):\n        self.tp9.append(tp9)\n        self.tp10.append(tp10)\n        if len(self.tp9) &lt; self.tp9.maxlen:\n            return\n\n        n = len(self.tp9)\n        mean9 = sum(self.tp9) \/ n\n        mean10 = sum(self.tp10) \/ n\n        var9 = sum((v - mean9) ** 2 for v in self.tp9) \/ n\n        var10 = sum((v - mean10) ** 2 for v in self.tp10) \/ n\n        amp = (math.sqrt(var9) + math.sqrt(var10)) \/ 2.0\n        self.last_amplitude = amp\n\n        if amp &gt;= self.threshold:\n            now = time.monotonic()\n            if now - self.last_fire_time &gt;= self.debounce:\n                self.last_fire_time = now\n                if self.on_clench:\n                    self.on_clench(amp)\n\n\ndef _bar(value: float, max_value: float, width: int = 40) -&gt; str:\n    filled = min(width, int(width * value \/ max_value))\n    return \"\u2588\" * filled + \"\u00b7\" * (width - filled)\n\n\ndef _run_standalone(port: int, threshold: float):\n    detector = JawClenchDetector(\n        threshold_uv=threshold,\n        on_clench=lambda amp: print(f\"\\n*** CLENCH ({amp:6.1f} \u00b5V) ***\"),\n    )\n\n    def handle_eeg(address, *args):\n        if len(args) &lt; 4:\n            return\n        tp9, _af7, _af8, tp10 = args&#091;0], args&#091;1], args&#091;2], args&#091;3]\n        detector.feed(tp9, tp10)\n\n    d = dispatcher.Dispatcher()\n    d.map(\"\/muse\/eeg\", handle_eeg)\n    server = osc_server.ThreadingOSCUDPServer((\"0.0.0.0\", port), d)\n\n    import threading\n    t = threading.Thread(target=server.serve_forever, daemon=True)\n    t.start()\n\n    print(f\"Listening on UDP {port}.  Threshold: {threshold:.0f} \u00b5V.\")\n    print(\"Live amplitude (TP9+TP10 RMS) \u2014 clench your jaw to test:\\n\")\n    bar_max = max(threshold * 2.5, 200.0)\n    try:\n        while True:\n            amp = detector.last_amplitude\n            marker = \"  &lt;-- TRIGGER\" if amp &gt;= threshold else \"\"\n            print(f\"\\r{_bar(amp, bar_max)} {amp:7.1f} \u00b5V{marker}   \", end=\"\", flush=True)\n            time.sleep(0.05)\n    except KeyboardInterrupt:\n        print(\"\\nStopped.\")\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Muse jaw clench detector\")\n    parser.add_argument(\"--port\", type=int, default=5000)\n    parser.add_argument(\"--threshold\", type=float, default=DEFAULT_THRESHOLD_UV)\n    args = parser.parse_args()\n    _run_standalone(args.port, args.threshold)\n\n\nif __name__ == \"__main__\":\n    main()\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">jump_detector.py<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>from collections import deque\nfrom pythonosc import dispatcher, osc_server\nimport argparse\nimport math\nimport threading\nimport time\n\nACC_RATE_HZ = 52\nDEFAULT_FREEFALL_G = 0.5\nDEFAULT_GROUNDED_G = 0.85\nDEFAULT_DEBOUNCE_SEC = 0.8\n\n\nclass JumpDetector:\n    def __init__(\n        self,\n        freefall_g: float = DEFAULT_FREEFALL_G,\n        grounded_g: float = DEFAULT_GROUNDED_G,\n        debounce_sec: float = DEFAULT_DEBOUNCE_SEC,\n        on_jump=None,\n    ):\n        self.freefall_g = freefall_g\n        self.grounded_g = grounded_g\n        self.debounce = debounce_sec\n        self.on_jump = on_jump\n        self.last_magnitude = 0.0\n        self.last_fire = 0.0\n        self._airborne = False\n\n    def feed(self, x: float, y: float, z: float):\n        mag = math.sqrt(x * x + y * y + z * z)\n        self.last_magnitude = mag\n\n        if not self._airborne:\n            if mag &lt; self.freefall_g:\n                now = time.monotonic()\n                if now - self.last_fire &gt;= self.debounce:\n                    self._airborne = True\n                    self.last_fire = now\n                    if self.on_jump:\n                        self.on_jump(mag)\n        else:\n            if mag &gt; self.grounded_g:\n                self._airborne = False\n\n\ndef _bar(value, max_value, width=40):\n    filled = min(width, int(width * value \/ max_value))\n    return \"\u2588\" * filled + \"\u00b7\" * (width - filled)\n\n\ndef _run_standalone(port, freefall):\n    detector = JumpDetector(\n        freefall_g=freefall,\n        on_jump=lambda mag: print(f\"\\n*** JUMP (freefall {mag:.2f} g) ***\"),\n    )\n\n    def handle_acc(address, *args):\n        if len(args) &gt;= 3:\n            detector.feed(args&#091;0], args&#091;1], args&#091;2])\n\n    d = dispatcher.Dispatcher()\n    d.map(\"\/muse\/acc\", handle_acc)\n    server = osc_server.ThreadingOSCUDPServer((\"0.0.0.0\", port), d)\n    t = threading.Thread(target=server.serve_forever, daemon=True)\n    t.start()\n\n    print(f\"Listening on UDP {port}.  Freefall trigger: &lt; {freefall:.2f} g\")\n    try:\n        while True:\n            mag = detector.last_magnitude\n            state = \"AIRBORNE\" if detector._airborne else \"grounded\"\n            marker = \"  &lt;-- FREEFALL\" if mag &lt; freefall else \"\"\n            print(f\"\\r{_bar(mag, 4.0)} {mag:.3f} g  &#091;{state}]{marker}   \",\n                  end=\"\", flush=True)\n            time.sleep(0.05)\n    except KeyboardInterrupt:\n        print(\"\\nStopped.\")\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"Muse jump detector\")\n    parser.add_argument(\"--port\", type=int, default=5000)\n    parser.add_argument(\"--freefall\", type=float, default=DEFAULT_FREEFALL_G)\n    args = parser.parse_args()\n    _run_standalone(args.port, args.freefall)\n\n\nif __name__ == \"__main__\":\n    main()\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">bci_flappy.py<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>import argparse\nimport threading\n\nimport pygame\nfrom pythonosc import dispatcher, osc_server\n\nfrom flappy import Game\nfrom jaw_detector import JawClenchDetector\nfrom jump_detector import JumpDetector\n\nCLENCH_EVENT = pygame.USEREVENT + 1\nJUMP_EVENT   = pygame.USEREVENT + 2\n\n\ndef start_osc_listener(port, clench_threshold, jump_threshold):\n    def on_clench(amp):\n        pygame.event.post(pygame.event.Event(CLENCH_EVENT, value=amp))\n\n    def on_jump(mag):\n        pygame.event.post(pygame.event.Event(JUMP_EVENT, value=mag))\n\n    clench_det = JawClenchDetector(threshold_uv=clench_threshold, on_clench=on_clench)\n    jump_det   = JumpDetector(freefall_g=jump_threshold, on_jump=on_jump)\n\n    def handle_eeg(_addr, *args):\n        if len(args) &gt;= 4:\n            clench_det.feed(args&#091;0], args&#091;3])\n\n    def handle_acc(_addr, *args):\n        if len(args) &gt;= 3:\n            jump_det.feed(args&#091;0], args&#091;1], args&#091;2])\n\n    d = dispatcher.Dispatcher()\n    d.map(\"\/muse\/eeg\", handle_eeg)\n    d.map(\"\/muse\/acc\", handle_acc)\n\n    server = osc_server.ThreadingOSCUDPServer((\"0.0.0.0\", port), d)\n    threading.Thread(target=server.serve_forever, daemon=True).start()\n\n    print(f\"OSC listening on UDP {port}\")\n    print(f\"  Jaw clench threshold : {clench_threshold:.0f} \u00b5V\")\n    print(f\"  Jump threshold       : {jump_threshold:.2f} g\")\n    print(f\"  SPACE \/ click also work as fallback\\n\")\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"BCI Flappy Bird\")\n    parser.add_argument(\"--port\",              type=int,   default=5000)\n    parser.add_argument(\"--clench-threshold\",  type=float, default=45.0,\n                        help=\"Jaw clench RMS \u00b5V threshold\")\n    parser.add_argument(\"--jump-threshold\",    type=float, default=1.8,\n                        help=\"Jump acceleration magnitude in g\")\n    args = parser.parse_args()\n\n    game = Game()\n    start_osc_listener(args.port, args.clench_threshold, args.jump_threshold)\n\n    running = True\n    while running:\n        dt = game.clock.tick(60) \/ 1000.0\n        for e in pygame.event.get():\n            if e.type == CLENCH_EVENT:\n                print(f\"  jaw  {e.value:6.1f} \u00b5V -&gt; flap\")\n                game.jump()\n            elif e.type == JUMP_EVENT:\n                print(f\"  jump {e.value:.2f} g  -&gt; flap\")\n                game.jump()\n            elif not game.handle_event(e):\n                running = False\n        game.update(dt)\n        game.draw()\n    pygame.quit()\n\n\nif __name__ == \"__main__\":\n    main()\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Appendix B &#8211; <\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"756\" src=\"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-5.13.58-PM-1024x756.png\" alt=\"\" class=\"wp-image-217\" srcset=\"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-5.13.58-PM-1024x756.png 1024w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-5.13.58-PM-300x222.png 300w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-5.13.58-PM-768x567.png 768w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-5.13.58-PM-1536x1134.png 1536w, https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-content\/uploads\/sites\/19\/2026\/06\/Screenshot-2026-06-04-at-5.13.58-PM-2048x1512.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">A Kanban board was used to organize the project&#8217;s tasks. Daily logs were used to keep track of progress. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Project Goals What problem did you set out to solve? Every day, some people wake up unable to feel anything but their head. To eat breakfast, they are fed by a caretaker because their hands are paralyzed. Around 15.4 million people deal with Spinal Cord Injuries(SCIs) in the world. This is such a significant problem [&hellip;]<\/p>\n","protected":false},"author":17,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-204","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-json\/wp\/v2\/posts\/204","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-json\/wp\/v2\/users\/17"}],"replies":[{"embeddable":true,"href":"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-json\/wp\/v2\/comments?post=204"}],"version-history":[{"count":9,"href":"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-json\/wp\/v2\/posts\/204\/revisions"}],"predecessor-version":[{"id":220,"href":"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-json\/wp\/v2\/posts\/204\/revisions\/220"}],"wp:attachment":[{"href":"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-json\/wp\/v2\/media?parent=204"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-json\/wp\/v2\/categories?post=204"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wp.stgeorges.bc.ca\/akbarj\/wp-json\/wp\/v2\/tags?post=204"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}