This project can be used with a Pupil Labs Neon eye tracker to record and visualize data as a user browses a webpage. Gaze data is mapped to coordinates for the webpage and for individual AOIs within a page.
Install the python package and initialize Playwright
pip install git+https://github.com/pupil-labs/web-aois.git
playwright install
-
Create an AOI Definitions file
pl-web-aois-define
- This will open a web browser. Navigate to the page you intend to study.
- Move the mouse over an AOI element. A red box will appear indicating the extents of the element.
- Note: To select the enclosing element of the currently highlighted element, press the
p
key.
- Note: To select the enclosing element of the currently highlighted element, press the
- Right-click to create the AOI. You will be prompted for a name.
- Once all of the AOIs for this page are defined, click on the
Save
button under the list of AOIs.
NOTE: At this time on most sites (not on SPAs, for example), navigating to a new page will reset the list of AOIs, so you must define and save AOIs one page at a time. You can then manually combine the definitions.
NOTE: The definition tool uses xpaths to identify web elements, which is not recommended by Playwright developers because it relies on the underlying structure of the web page which may not be consistent. If that structure changes (which you may not be able to tell just by looking at the page), you will need to re-define the AOIs. For more reliable definitions, you can use locators by manually specifying your AOI definitions.
-
Collect data
a. Record a browsing session. This will connect to the companion app and start a recording. The recording will be stopped when the browser window is closed.
You can optionally specify a URL to start with. If you do not, the first URL in the AOI definitions file will be used.
pl-web-aois-record path-to-aoi-defs.json [https://example.com/]
b. Download and extract the recording to your PC. Recordings can be transferred from the device over USB or downloaded from Pupil Cloud (use "Native Recording Data").
-
Process your recording to generate new CSV files that have gaze mapped to web page coordinates and individual AOI coordinates
pl-web-aois-process path-to-recording process-output-path
-
Visualize your data
a. Collect screenshots
pl-web-aois-screenshots path-to-aoi-defs.json screenshots-output-path
b. Create visualizations
pl-web-aois-visualize process-output-path screenshots-output-path
The AOI definitions file is a JSON-formatted structure that describes which elements on which webpages should be considered AOI's. The file follows this format:
{
"[url]":{
"[aoi name]": [
{
"type": "[selector type]",
"args": {
"[arg name]": "[arg value]",
...
},
},{
"type": "[selector type]",
"args": {
"[arg name]": "[arg value]",
...
}
},{
...
}
]
...
}
}
url
indicates on which page the AOIs defined within it should be consideredaoi name
is up to you, but avoid characters that aren't friendly for file names- The value for each AOI is an array of sequentially applied selector definitions.
- The first selector definition in each AOI array must have a
type
of one of the following: - Subsequent selectors are applied to the previous selector result to identify child elements therein. Except for the first selector definition in each AOI array, the following
type
values are allowed in addition to the ones above:
The corresponding links for each of the selector types documents arguments for each selector type. Some arguments accept regular expressions (e.g., filter(has_text=re.compile(...))
), but JSON does not support regular expression literals like JavaScript or Python do. To specify that an argument should be interpreted has a regular expression, append (re)
to the argument name like so:
...
{
"type": "filter",
"args": {
"has_text(re)": "^Lorem ipsum dolor.$"
}
}
...
Under the hood, AOIs on a webpage are identified using Playwright's locators. You can use Playwright's codegen
tool to determine the appropriate values for type
and its args
when setting up your AOIs.
- Launch the codegen tool
playwright codegen "https://example.com/"
- Click the
Pick Locator
tool on the toolbar at the top of the browser window - Hover the mouse over the element you wish to use as an AOI
- Note the tooltip that appears below the AOI. It will be something like
get_by_text("The Neon Companion app can")
,get_by_role("img", name="Fixations")
,locator("video")
, etc.- If it starts with
get_by_
, then the following word indicates the value for type - If it starts with
locator
, then the type will belocator
. - Values inside the parentheses are the arguments. You may probably need to look up the name of the first argument.
- If it starts with
- The dot
.
operator separates values for the selector definition array. In the example below, theour_tools_section
AOI had a tooltip that read:locator("div").filter(has_text="Our tools make the hidden").nth(3)
.
Here's an example that defines two simple AOIs for one webpage and one complex AOI for another webpage:
{
"https://docs.pupil-labs.com/neon/data-collection/data-streams/": {
"gaze_paragraph": [{
"type": "text",
"args": {
"text": "The Neon Companion app can"
}
}],
"fixations_image": [{
"type": "role",
"args": {
"role": "img",
"name": "Fixations"
}
}]
},
"https://pupil-labs.com/": {
"our_tools_section": [
{
"type": "locator",
"args": {
"selector": "div"
}
},{
"type": "filter",
"args": {
"has_text": "Our tools make the hidden"
}
},{
"type": "nth",
"args": {
"index": 3
}
}
]
}
}
A file named gazes.csv
file will be generated which represents mapped gaze data to the webpage in full. It includes the following columns:
Column | Description |
---|---|
timestamp [ns] | UTC timestamp in nanoseconds of the sample |
x [norm] | X-coordinate of the mapped gaze point to the browser window content in normalized space (0-1 ) |
y [norm] | Y-coordinate of the mapped gaze point to the browser window content in normalized space (0-1 ) |
window x [px] | X-coordinate of the mapped gaze point to the browser window content in pixels (ignores page scroll) |
window y [px] | Y-coordinate of the mapped gaze point to the browser window content in pixels (ignores page scroll) |
page x [px] | X-coordinate of the mapped gaze point to the web page in pixels |
page y [px] | Y-coordinate of the mapped gaze point to the web page in pixels |
For each AOI, a aoi-[AOI_NAME].csv
file will be generated which represents mapped gaze data that lands on that particular AOI. It includes the following columns:
Column | Description |
---|---|
timestamp [ns] | UTC timestamp in nanoseconds of the sample |
x [norm] | X-coordinate of the mapped gaze point to the AOI in normalized space (0-1 ) |
y [norm] | Y-coordinate of the mapped gaze point to the AOI in normalized space (0-1 ) |
x [px] | X-coordinate of the mapped gaze point to the AOI in pixels |
y [px] | Y-coordinate of the mapped gaze point to the AOI in pixels |
If heatmaps are generated per-recording, you will find two .png
files for the whole page (heatmap-gazes-overlaid.png
and heatmap-gazes-transparent.png
) and two .png
files for each AOI (heatmap-aoi-[AOI_NAME]-overlaid.png
and heatmap-aoi-[AOI_NAME]-transparent.png
). The transparent images include only the heatmap data, while the overlaid versions show the heatmap superimposed on captures of the webpage and AOIs.
- Although Playwright supports several browsers, this has only been configured and tested with Chromium.
- At this time browsing must be limited to a single window and tab. Gaze mapping in other windows or tabs is currently unsupported and results are undefined.
- Firefox can't track new tabs created with
Ctrl+t
. - Chromium doesn't trigger focus/visibility changes (switching between tabs) consistently.