From 78e046b3c30dd3a3a4d0ba73bc0594e2b2a4c3c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3is=C3=ADn=20Sharma?= Date: Mon, 5 Jan 2026 11:05:57 +0000 Subject: [PATCH 1/4] Update profile --- content/authors/roisin-sharma/_index.md | 17 +++++++++-------- .../roisin-sharma/{avatar.jpeg => avatar.jpg} | Bin 2 files changed, 9 insertions(+), 8 deletions(-) rename content/authors/roisin-sharma/{avatar.jpeg => avatar.jpg} (100%) diff --git a/content/authors/roisin-sharma/_index.md b/content/authors/roisin-sharma/_index.md index fbfe2597f..7a238365e 100644 --- a/content/authors/roisin-sharma/_index.md +++ b/content/authors/roisin-sharma/_index.md @@ -2,12 +2,12 @@ title: Róisín Sharma authors: - roisin-sharma -bio: Placement student and research assistant at the University of Sussex +bio: Placement Student and Research Assistant at the Reality Bending Lab role: Placement Student
University of Sussex
social: - icon: github icon_pack: fab - link: https://github.com/sh-roisin + link: https://github.com/RoisinSharma - icon: envelope icon_pack: fas link: mailto:rs843@sussex.ac.uk @@ -15,14 +15,15 @@ social: icon_pack: fab link: https://www.researchgate.net/profile/Roisin-Sharma interests: -- Statistics -- Consciousness +- Statistical Analysis +- Cognitive Science +- Learning languages - Chess (strictly informally!) education: - courses: - - course: BSc Psychology - institution: University of Sussex - year: 2023 - 2027 + courses: + - course: BSc Psychology + institution: University of Sussex + year: 2023 - 2027 user_groups: - Research assistants superuser: false diff --git a/content/authors/roisin-sharma/avatar.jpeg b/content/authors/roisin-sharma/avatar.jpg similarity index 100% rename from content/authors/roisin-sharma/avatar.jpeg rename to content/authors/roisin-sharma/avatar.jpg From 3b0566947dea3aef33e79d98c307366788525adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3is=C3=ADn=20Sharma?= Date: Fri, 9 Jan 2026 10:35:24 +0000 Subject: [PATCH 2/4] Create index.md --- .../post/2026-01-09-EventTriggers/index.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 content/post/2026-01-09-EventTriggers/index.md diff --git a/content/post/2026-01-09-EventTriggers/index.md b/content/post/2026-01-09-EventTriggers/index.md new file mode 100644 index 000000000..3068963f6 --- /dev/null +++ b/content/post/2026-01-09-EventTriggers/index.md @@ -0,0 +1,50 @@ +--- +authors: +- roisin-sharma +categories: +- Reality Bending Lab +- University of Sussex +date: "2026-01-09" +image: + caption: '' + placement: 0 +title: "How to send event triggers to Lab Streaming Layer from JsPsych" +subtitle: "Learn how to set up DataPipe to collect and save data in OSF, including creating an OSF project, linking it to DataPipe, configuring data collection, and saving data from an experiment hosted on GitHub." +summary: "Learn how to set up DataPipe to collect and save data in OSF, including creating an OSF project, linking it to DataPipe, configuring data collection, and saving data from an experiment hosted on GitHub." +tags: +- LSL +- Data Collection +- JsPsych +- Reality Bending Lab +- ReBeL +- University of Sussex +- Psychology +--- + +Hello there! 👋 Let's learn how to send event triggers to Lab Streaming Layer (LSL) from JsPsych. + +Lets start with some basics! + +IDEAS: + +- add image of lux marker and show diagram from validation experiments to illustrate that it's comparatively the 'best' in terms of consistency of delay making it yield more accurate data + +- You can find a simple example of this in action from our recent validation experiments, which compared when the event triggers (referred to as 'markers') picked up the screen changing from white to black, to the Bitalino LUX: . + +## What does this mean and when is this useful? + +Lab streaming layer (LSL) is a system used to receive, synchronise and stream signals from multiple inputs during experiments. LSL is designed to help researchers easily compare their data across multiple technologies, as time synchrony is integral for making valid comparisons. + +When collecting data on stimulus response during experiments, it's important that the stimulus onset is recorded precisely, especially if this is mapped onto physiological responses as this has implications on how we interpret our data. One good example of a use case is that you have a Muse headband or any other device that can scream via LSL, and you want to precisely mark events in it. + +Event triggers are coded into JsPsych online experiments to accurately mark events, such as when a stimulus appeared on the screen. + +This tutorial will explain how to set this up for an experiment situated on GitHub, although you can adapt this for your hosting platform. + +## How to set up event triggers + +1. **Create the LSL bridge in your repository:** + +2. **Synchronise your event triggers in the html script of your experiment:** + +## How to record event triggers during your experiment From c0f1f0a47c731ad704a4b1d86ccb1f0a4b62a0d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=B3is=C3=ADn=20Sharma?= Date: Sun, 11 Jan 2026 13:16:47 +0000 Subject: [PATCH 3/4] Event trigger blog 1st draft --- .../post/2026-01-09-EventTriggers/index.md | 95 ++++++++++++++++--- 1 file changed, 82 insertions(+), 13 deletions(-) diff --git a/content/post/2026-01-09-EventTriggers/index.md b/content/post/2026-01-09-EventTriggers/index.md index 3068963f6..8793cdb3e 100644 --- a/content/post/2026-01-09-EventTriggers/index.md +++ b/content/post/2026-01-09-EventTriggers/index.md @@ -4,13 +4,13 @@ authors: categories: - Reality Bending Lab - University of Sussex -date: "2026-01-09" +date: "2026-01-11" image: caption: '' placement: 0 title: "How to send event triggers to Lab Streaming Layer from JsPsych" -subtitle: "Learn how to set up DataPipe to collect and save data in OSF, including creating an OSF project, linking it to DataPipe, configuring data collection, and saving data from an experiment hosted on GitHub." -summary: "Learn how to set up DataPipe to collect and save data in OSF, including creating an OSF project, linking it to DataPipe, configuring data collection, and saving data from an experiment hosted on GitHub." +subtitle: "Understand the steps involving in setting up event triggers for your JsPsych experiments, including creating the bridge to Lab Streaming Layer, ensuring synchronisation with other signals and highly accurate timestamps." +summary: "Understand the steps involving in setting up event triggers for your JsPsych experiments, including creating the bridge to Lab Streaming Layer, ensuring synchronisation with other signals and highly accurate timestamps." tags: - LSL - Data Collection @@ -25,26 +25,95 @@ Hello there! 👋 Let's learn how to send event triggers to Lab Streaming Layer Lets start with some basics! -IDEAS: - -- add image of lux marker and show diagram from validation experiments to illustrate that it's comparatively the 'best' in terms of consistency of delay making it yield more accurate data - -- You can find a simple example of this in action from our recent validation experiments, which compared when the event triggers (referred to as 'markers') picked up the screen changing from white to black, to the Bitalino LUX: . - ## What does this mean and when is this useful? Lab streaming layer (LSL) is a system used to receive, synchronise and stream signals from multiple inputs during experiments. LSL is designed to help researchers easily compare their data across multiple technologies, as time synchrony is integral for making valid comparisons. When collecting data on stimulus response during experiments, it's important that the stimulus onset is recorded precisely, especially if this is mapped onto physiological responses as this has implications on how we interpret our data. One good example of a use case is that you have a Muse headband or any other device that can scream via LSL, and you want to precisely mark events in it. -Event triggers are coded into JsPsych online experiments to accurately mark events, such as when a stimulus appeared on the screen. +Event triggers are coded into JsPsych online experiments to accurately mark events, such as when a stimulus appeared on the screen. We have found this method to yield the most precise timestamps of events, compared to alternative methods such as using the Bitalino LUX. This tutorial will explain how to set this up for an experiment situated on GitHub, although you can adapt this for your hosting platform. +This blog will help you understand the set-up for event triggers. For an example of this in action, refer to . This experiment recorded markers on a screen turning from white to black- you may want to follow along with lsl_bridge.py and blackwhite_jspsych.html [^1] for the full implementation. + +[^1]: Special shoutout to our placement student Oliver Collins for preparing these scripts! + ## How to set up event triggers -1. **Create the LSL bridge in your repository:** +#### Requirements: + +- **Your experiment is in-person:** In order to use these event triggers, you will need to run your experiment on a local host server, which will need to be manually set up for each trial. Therefore, this setup is intended for in-person experiments that are led by a researcher to set up the participant's screen. + +- **The researcher and the participant(s) each have a computer:** The participant's computer will send the markers to the researcher's computer, which will be used to record the events (and all other signals using LSL) during the trial. + +#### The LSL bridge script: + +The LSL bridge will send the markers to LSL- it 'listens' for messages from the browser that the participant is doing the experiment from, and converts them into 'markers' for your recording software (such as LabRecorder) will receive. + +1. Configuration + + - **Imports**: Load in standard python libraries for creating a web server (`http.server`, `urllib`) and the `mne_lsl` library to handle the data streaming. + + - **Configure Variables**: Name the event trigger stream so you can find it in LabRecorder, e.g. `LSL_STREAM_NAME = "jsPsychMarkers"`. + + - Add `SERVER_HOST = "0.0.0.0"` into the script to tell the server to listen to all available network interfaces, allowing the participant's computer to communicate with the researcher's. + + - Specify the port for the html script to go to e.g. `SERVER_PORT = 5000`. You will add this port into the html script that holds the online experiment to, in order to send the experiment to this python script. + +2. Create the LSL outlet for event triggers + + - Define the metadata for the stream: `info = StreamInfo( name=LSL_STREAM_NAME...` + + - Create the outlet object that will push data out to the network: `outlet = StreamOutlet(info)`. + +3. Create the request handler to define what happens when the participant's browser contacts the server. + + - The sync route: + + - `if path == "/sync":`: Checks if the browser is asking to synchronize clocks. + + - `ts = local_clock()`: Grabs the current high-precision time from the LSL clock on the Recording Machine. + + - `self.wfile.write(...)`: Sends this timestamp back to the browser. The browser needs this to calculate the time difference (offset) between the two computers. + + - The marker route: + + - `elif path == "/marker"`: Checks if the browser is trying to send an event marker. + - It extracts `value` (the marker name, e.g., "1") and `ts` (the timestamp calculated by the browser) from the URL parameters. + - `outlet.push_sample([value], ts)`: This is the most important line. It injects the marker into the LSL stream *using the timestamp provided by the browser*. This ensures that even if there is network lag, the timestamp recorded in the EEG data remains accurate to when the event actually happened on the participant's screen. + +4. Run the Server + + - `run_server()`: Starts the HTTP server and prints a confirmation message that it is ready for LabRecorder. + - `threading.Thread(...)`: Runs the server in a separate thread so it doesn't block the main Python process. + +#### Synchronise your event triggers in the html script: + +This is 'promise-based', meaning it is coded to wait until it receives a signal from the participant's computer. This javascript code is designed to track the exact time it is on the participant's computer and send markers precisely aligned to that time. + +1. Synchronisation + + - `var lslBaseTime = null`: A variable to store the calculated time difference between the two computers. + + - The function `syncLSL() {...}` can be looped three times to get an average reading. + + - Get the time of the marker: `fetch("http://.../sync")` + + - Record `startPerf` (when the request left) and `endPerf` (when the answer came back). + + - You can assume the server received the message exactly halfway between start and end (`perfMid`). + + - Calculates the difference between the browser's clock and the LSL clock: `offsets.push(lslTime - perfMid / 1000)`. + + - Finally, average these offsets into `lslBaseTime`. Now the browser knows how to convert its own time to LSL time. + +2. Sending markers + + - Safety check in case sync fails: `if (lslBaseTime === null)` can be coded to send markers based on the timing from the participant's computer without synchronisation, which is less accurate but better than nothing. + + - Code the mathematical logic to account for the offset in time for the recording computer to receive the marker, so the generated timestamp aligns with the recording computer's timestamp: `var ts = lslBaseTime + performance.now() / 1000`. -2. **Synchronise your event triggers in the html script of your experiment:** + - Send the marker name and this calculated timestamp to the python bridge: `fetch(url)`. -## How to record event triggers during your experiment +You are now ready to record event triggers during your experiment. For a guide on how to set this up, you can refer to the README.md file of: . From 82920fa52d682a38ecc1ecddd4d9642561e10d1a Mon Sep 17 00:00:00 2001 From: DominiqueMakowski Date: Thu, 22 Jan 2026 16:54:59 +0000 Subject: [PATCH 4/4] minor --- content/jobs/assistant.md | 4 + .../post/2026-01-09-EventTriggers/index.md | 118 ++++++++++++++++-- 2 files changed, 114 insertions(+), 8 deletions(-) diff --git a/content/jobs/assistant.md b/content/jobs/assistant.md index 54142c613..31e2a8e17 100644 --- a/content/jobs/assistant.md +++ b/content/jobs/assistant.md @@ -57,6 +57,10 @@ Undergraduates residing in the UK can apply for a paid 6 weeks (30hr per week) i More information [**here**](https://southcoastbiosciencesdtp.ac.uk/undergraduate-summer-studentship-programme/). +## International Junior Research Associate (IJRA) + +- See here: https://www.sussex.ac.uk/suro/current/ijra + ## Other Bursaries diff --git a/content/post/2026-01-09-EventTriggers/index.md b/content/post/2026-01-09-EventTriggers/index.md index 8793cdb3e..bb06a388e 100644 --- a/content/post/2026-01-09-EventTriggers/index.md +++ b/content/post/2026-01-09-EventTriggers/index.md @@ -27,7 +27,7 @@ Lets start with some basics! ## What does this mean and when is this useful? -Lab streaming layer (LSL) is a system used to receive, synchronise and stream signals from multiple inputs during experiments. LSL is designed to help researchers easily compare their data across multiple technologies, as time synchrony is integral for making valid comparisons. +Lab streaming layer (LSL) is a system used to receive, synchronise and stream signals from multiple inputs during experiments. LSL is designed to help researchers easily compare their data across multiple technologies, as time synchrony is integral for making meaningful analyses. When collecting data on stimulus response during experiments, it's important that the stimulus onset is recorded precisely, especially if this is mapped onto physiological responses as this has implications on how we interpret our data. One good example of a use case is that you have a Muse headband or any other device that can scream via LSL, and you want to precisely mark events in it. @@ -41,15 +41,109 @@ This blog will help you understand the set-up for event triggers. For an example ## How to set up event triggers -#### Requirements: +### Requirements - **Your experiment is in-person:** In order to use these event triggers, you will need to run your experiment on a local host server, which will need to be manually set up for each trial. Therefore, this setup is intended for in-person experiments that are led by a researcher to set up the participant's screen. -- **The researcher and the participant(s) each have a computer:** The participant's computer will send the markers to the researcher's computer, which will be used to record the events (and all other signals using LSL) during the trial. +- **You have two computers, one for the participant, and for recording:** The participant's computer will display the experiment andsend the markers to the researcher's computer, which will be used to capture the LSL streams record the events. + +### The LSL bridge script + +The LSL bridge Python script is the one responsible for actually sending the markers to LSL- it 'listens' for messages from the browser that the participant is doing the experiment from, and converts them into 'markers' for your recording software (such as LabRecorder) will receive. + +
+ +See an example of a full script + +```python +from http.server import BaseHTTPRequestHandler, HTTPServer +from urllib.parse import urlparse, parse_qs +from mne_lsl.lsl import StreamInfo, StreamOutlet, local_clock +import threading + +# --------------------------------------------------------------------- +# CONFIG +# --------------------------------------------------------------------- +LSL_STREAM_NAME = "jsPsychMarkers" +LSL_STREAM_TYPE = "Markers" +LSL_SOURCE_ID = "jspsych-lsl-bridge" +SERVER_HOST = "0.0.0.0" +SERVER_PORT = 5000 +# --------------------------------------------------------------------- + +# Create an LSL outlet for event markers +info = StreamInfo( + name=LSL_STREAM_NAME, + stype=LSL_STREAM_TYPE, + n_channels=1, + sfreq=0, + dtype="string", + source_id=LSL_SOURCE_ID, +) + +desc = info.desc +desc.append_child_value("manufacturer", "jsPsych") +channels = desc.append_child("channels") +ch = channels.append_child("channel") +ch.append_child_value("label", "JsPsychMarker") +ch.append_child_value("unit", "string") +ch.append_child_value("type", "Marker") + +outlet = StreamOutlet(info) + +# --------------------------------------------------------------------- +# HTTP Request Handler +# --------------------------------------------------------------------- +class MarkerHandler(BaseHTTPRequestHandler): + def do_GET(self): + parsed = urlparse(self.path) + params = parse_qs(parsed.query) + path = parsed.path + + if path == "/sync": + # Return current LSL clock to JS + ts = local_clock() + self.send_response(200) + self.end_headers() + self.wfile.write(str(ts).encode()) + + elif path == "/marker": + value = params.get("value", ["1"])[0] + ts_js = params.get("ts", [None])[0] + + if ts_js is not None: + ts = float(ts_js) + else: + ts = local_clock() + + outlet.push_sample([value], ts) + print(f"→ Marker {value} @ {ts:.6f}") + + self.send_response(200) + self.end_headers() + self.wfile.write(b"OK") + + else: + self.send_response(404) + self.end_headers() + +# --------------------------------------------------------------------- +def run_server(): + server_address = (SERVER_HOST, SERVER_PORT) + httpd = HTTPServer(server_address, MarkerHandler) + print(f"\n[LSL Bridge] Serving on http://{SERVER_HOST}:{SERVER_PORT}") + print(f"[LSL Bridge] Stream '{LSL_STREAM_NAME}' ready for LabRecorder.\n") + httpd.serve_forever() + +# --------------------------------------------------------------------- +if __name__ == "__main__": + server_thread = threading.Thread(target=run_server) + server_thread.start() +``` +
+ +### Configuration of the Python Script -#### The LSL bridge script: - -The LSL bridge will send the markers to LSL- it 'listens' for messages from the browser that the participant is doing the experiment from, and converts them into 'markers' for your recording software (such as LabRecorder) will receive. 1. Configuration @@ -88,9 +182,9 @@ The LSL bridge will send the markers to LSL- it 'listens' for messages from the - `run_server()`: Starts the HTTP server and prints a confirmation message that it is ready for LabRecorder. - `threading.Thread(...)`: Runs the server in a separate thread so it doesn't block the main Python process. -#### Synchronise your event triggers in the html script: +### Configuration of the JsPsych HTML script -This is 'promise-based', meaning it is coded to wait until it receives a signal from the participant's computer. This javascript code is designed to track the exact time it is on the participant's computer and send markers precisely aligned to that time. +The code sending triggers from the browser to the LSL bridge script is written in javascript. It is 'promise-based', meaning it is coded to wait until it receives a signal from the participant's computer. This javascript code is designed to track the exact time it is on the participant's computer and send markers precisely aligned to that time. 1. Synchronisation @@ -116,4 +210,12 @@ This is 'promise-based', meaning it is coded to wait until it receives a signal - Send the marker name and this calculated timestamp to the python bridge: `fetch(url)`. + +### Usage + +1. Open the terminal... +2. Run the expe... + + + You are now ready to record event triggers during your experiment. For a guide on how to set this up, you can refer to the README.md file of: .