@easyeyes_threshold/runtime 1.20260909.84286

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 New York University (Denis Pelli and the EasyEyes team)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # EasyEyes Threshold
2
+
3
+ 📖 [**Manual**](https://docs.google.com/document/d/e/2PACX-1vTTrqaSyva2afVupLchBjfTHc_YW5jAbEexGbudXMJ9xMKPBDA3nxQmHXa4wjnAoSVabeEA8T9CGIMa/pub)<br />
4
+ 📝 [**Parameter Glossary**](https://docs.google.com/spreadsheets/d/e/2PACX-1vQ8QswX_5h_oNS2Ly6VgoONGIxJHqDFjdZqWY_HUxH2Nr_LNkGDBL8FXz74l9BxVNR2AIXGhHir9GAd/pub?gid=1287694458&single=true&output=pdf)<br />
5
+ 🌏 [**Webapp**](https://easyeyes.app/experiment/) (beta)
6
+
7
+ A PsychoJS-based experiment generator for the measuring various psychometric thresholds, e.g., crowding. Please visit [https://easyeyes.app/experiment](https://easyeyes.app/experiment/) for more instructions.
8
+
9
+ ## Development
10
+
11
+ To clone this repo, use
12
+
13
+ ```shell
14
+ git clone --recurse-submodules https://github.com/EasyEyes/threshold.git
15
+ ```
16
+
17
+ Then, run the following for the initial setup
18
+
19
+ ```shell
20
+ npm install
21
+ npm run build
22
+ ```
23
+
24
+ To develop based on a specific experiment table, use
25
+
26
+ ```shell
27
+ npm run examples [tableName].[xlsx] # you may also run `npm run examples` to prepare for all tables in your examples/tables folder
28
+ npm start # interactive selection of built examples
29
+ npm start -- --name=[tableName] # start hot-reload development for specific example
30
+ ```
31
+
32
+ You need to rerun `npm run examples [your table name]` every time you edit the HTML or table files.
33
+
34
+ ## PsychoJS Version
35
+
36
+ We build EasyEyes Threshold with PsychoJS `2022.1.3`.
37
+
38
+ ## Vite Build System (Experimental)
39
+
40
+ We've added Vite as an experimental alternative to Webpack for faster builds. The existing Webpack setup remains fully functional.
41
+
42
+ ### New Scripts
43
+
44
+ - `npm run vite-start` - Start Vite development server
45
+ - `npm run vite-build` - Build with Vite
46
+ - `npm run vite-preview` - Preview Vite production build
47
+ - `npm run compare-builds` - Compare Webpack vs Vite build performance (requires Rust)
48
+
49
+ ### Performance Comparison
50
+
51
+ To compare build performance between Webpack and Vite:
52
+
53
+ ```bash
54
+ npm run compare-builds
55
+ ```
56
+
57
+ This Rust-based tool runs each build 3 times and provides detailed statistics including:
58
+
59
+ - Average build times
60
+ - Standard deviation
61
+ - Statistical significance
62
+ - Percentage improvement
63
+
64
+ ### Notes
65
+
66
+ - Vite configuration is in `vite.config.js`
67
+ - Both build systems output to the same `js/` directory
68
+ - The Rust comparison tool requires Rust to be installed
69
+ - Existing Webpack scripts (`npm start`, `npm run build`) remain unchanged
@@ -0,0 +1,133 @@
1
+ // /*! coi-serviceworker v0.1.6 - Guido Zuidhof, licensed under MIT */
2
+ // let coepCredentialless = false;
3
+ // if (typeof window === "undefined") {
4
+ // self.addEventListener("install", () => self.skipWaiting());
5
+ // self.addEventListener("activate", (event) =>
6
+ // event.waitUntil(self.clients.claim()),
7
+ // );
8
+
9
+ // self.addEventListener("message", (ev) => {
10
+ // if (!ev.data) {
11
+ // return;
12
+ // } else if (ev.data.type === "deregister") {
13
+ // self.registration
14
+ // .unregister()
15
+ // .then(() => {
16
+ // return self.clients.matchAll();
17
+ // })
18
+ // .then((clients) => {
19
+ // clients.forEach((client) => client.navigate(client.url));
20
+ // });
21
+ // } else if (ev.data.type === "coepCredentialless") {
22
+ // coepCredentialless = ev.data.value;
23
+ // }
24
+ // });
25
+
26
+ // self.addEventListener("fetch", function (event) {
27
+ // const r = event.request;
28
+ // const request =
29
+ // coepCredentialless && r.mode === "no-cors"
30
+ // ? new Request(r, {
31
+ // credentials: "omit",
32
+ // })
33
+ // : r;
34
+
35
+ // if (r.cache === "only-if-cached" && r.mode !== "same-origin") {
36
+ // return;
37
+ // }
38
+
39
+ // event.respondWith(
40
+ // fetch(request)
41
+ // .then((response) => {
42
+ // if (response.status === 0) {
43
+ // return response;
44
+ // }
45
+
46
+ // const newHeaders = new Headers(response.headers);
47
+ // newHeaders.set(
48
+ // "Cross-Origin-Embedder-Policy",
49
+ // coepCredentialless ? "credentialless" : "require-corp",
50
+ // );
51
+ // newHeaders.set("Cross-Origin-Opener-Policy", "same-origin");
52
+
53
+ // return new Response(response.body, {
54
+ // status: response.status,
55
+ // statusText: response.statusText,
56
+ // headers: newHeaders,
57
+ // });
58
+ // })
59
+ // .catch((e) => console.error(e)),
60
+ // );
61
+ // });
62
+ // } else {
63
+ // (() => {
64
+ // // You can customize the behavior of this script through a global `coi` variable.
65
+ // const coi = {
66
+ // shouldRegister: () => true,
67
+ // shouldDeregister: () => false,
68
+ // coepCredentialless: () => false,
69
+ // doReload: () => window.location.reload(),
70
+ // quiet: false,
71
+ // ...window.coi,
72
+ // };
73
+
74
+ // const n = navigator;
75
+
76
+ // if (n.serviceWorker && n.serviceWorker.controller) {
77
+ // n.serviceWorker.controller.postMessage({
78
+ // type: "coepCredentialless",
79
+ // value: coi.coepCredentialless(),
80
+ // });
81
+
82
+ // if (coi.shouldDeregister()) {
83
+ // n.serviceWorker.controller.postMessage({ type: "deregister" });
84
+ // }
85
+ // }
86
+
87
+ // // If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are
88
+ // // already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here.
89
+ // if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return;
90
+
91
+ // if (!window.isSecureContext) {
92
+ // !coi.quiet &&
93
+ // console.log(
94
+ // "COOP/COEP Service Worker not registered, a secure context is required.",
95
+ // );
96
+ // return;
97
+ // }
98
+
99
+ // // In some environments (e.g. Chrome incognito mode) this won't be available
100
+ // if (n.serviceWorker) {
101
+ // n.serviceWorker.register(window.document.currentScript.src).then(
102
+ // (registration) => {
103
+ // !coi.quiet &&
104
+ // console.log(
105
+ // "COOP/COEP Service Worker registered",
106
+ // registration.scope,
107
+ // );
108
+
109
+ // registration.addEventListener("updatefound", () => {
110
+ // !coi.quiet &&
111
+ // console.log(
112
+ // "Reloading page to make use of updated COOP/COEP Service Worker.",
113
+ // );
114
+ // coi.doReload();
115
+ // });
116
+
117
+ // // If the registration is active, but it's not controlling the page
118
+ // if (registration.active && !n.serviceWorker.controller) {
119
+ // !coi.quiet &&
120
+ // console.log(
121
+ // "Reloading page to make use of COOP/COEP Service Worker.",
122
+ // );
123
+ // coi.doReload();
124
+ // }
125
+ // },
126
+ // (err) => {
127
+ // !coi.quiet &&
128
+ // console.error("COOP/COEP Service Worker failed to register:", err);
129
+ // },
130
+ // );
131
+ // }
132
+ // })();
133
+ // }
Binary file
@@ -0,0 +1,22 @@
1
+ .form-input-btn {
2
+ font-size: 1.2rem;
3
+ margin: 0 0rem;
4
+ /* width: 5rem; */
5
+ height: 3rem !important;
6
+ outline: none;
7
+ border: none !important;
8
+ border-radius: 7px !important;
9
+ font-weight: 700 !important;
10
+ background-color: #ddd !important;
11
+ }
12
+
13
+ .form-input-btn:hover {
14
+ outline: 2px solid #999 !important;
15
+ outline-offset: 0.2rem !important;
16
+ }
17
+
18
+ .form-input-btn:active {
19
+ outline: 2px solid #999 !important;
20
+ outline-offset: 0.2rem !important;
21
+ background-color: #bbb !important;
22
+ }
@@ -0,0 +1,17 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Peripheral Display</title>
7
+ <link rel="stylesheet" href="multipleDisplay.css" />
8
+ </head>
9
+ <body style="margin: 0">
10
+ <script src="https://cdn.jsdelivr.net/npm/remote-calibrator@0.9.146"></script>
11
+ <script src="./peripheralDisplay.js" type="module"></script>
12
+ <!-- <div
13
+ id="rc-panel-holder"
14
+ style="z-index: 99999; width: calc(100% - 2rem); margin: 0 1rem"
15
+ ></div> -->
16
+ </body>
17
+ </html>
@@ -0,0 +1,37 @@
1
+ let rc = null;
2
+
3
+ async function runRemoteCalibrator() {
4
+ rc = new RemoteCalibrator();
5
+
6
+ rc.init();
7
+ console.log("RC LD", rc.languageData);
8
+ const tasks = [
9
+ {
10
+ name: "screenSize",
11
+ options: { fullscreen: true, check: false },
12
+ },
13
+ ];
14
+ await new Promise(async (resolve) => {
15
+ console.log("Remote Calibrator is running...");
16
+ //sleep for 2 seconds to make sure rc is loaded
17
+ await new Promise((res) => setTimeout(res, 2000));
18
+ rc.panel(
19
+ tasks,
20
+ "#rc-panel-holder",
21
+ {
22
+ debug: false,
23
+ i18n: true,
24
+ },
25
+ () => {
26
+ rc.removePanel();
27
+ rc._removeBackground();
28
+ console.log("Remote Calibrator is done");
29
+ resolve();
30
+ },
31
+ null,
32
+ null,
33
+ null,
34
+ );
35
+ });
36
+ }
37
+ // await runRemoteCalibrator();
@@ -0,0 +1,152 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
6
+
7
+ <!-- Load serviceWorker as soon as possible -->
8
+ <!-- We explicity enable COOP/COEP to use ffmpeg.wasm -->
9
+ <!-- https://dev.to/stefnotch/enabling-coop-coep-without-touching-the-server-2d3n -->
10
+ <!-- https://github.com/gzuidhof/coi-serviceworker -->
11
+ <script src="coi-serviceworker.js"></script>
12
+ <script
13
+ src="https://js.sentry-cdn.com/8d5c414335e8ff6ebf585b7204830e5f.min.js"
14
+ crossorigin="anonymous"
15
+ data-lazy="no"
16
+ ></script>
17
+
18
+ <title>EasyEyes Study</title>
19
+ <link rel="icon" type="image/x-icon" href="components/images/favicon.ico" />
20
+
21
+ <!-- styles -->
22
+ <link
23
+ rel="stylesheet"
24
+ href="https://cdn.jsdelivr.net/npm/jquery-ui-dist@1.12.1/jquery-ui.min.css"
25
+ />
26
+
27
+ <link
28
+ rel="stylesheet"
29
+ href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
30
+ integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
31
+ crossorigin="anonymous"
32
+ />
33
+ <link
34
+ rel="stylesheet"
35
+ href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.6.1/font/bootstrap-icons.css"
36
+ />
37
+ <script
38
+ src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js"
39
+ integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF"
40
+ crossorigin="anonymous"
41
+ ></script>
42
+ <meta
43
+ http-equiv="Delegate-CH"
44
+ content="sec-ch-ua-full-version-list https://cloud.51degrees.com; sec-ch-ua-model https://cloud.51degrees.com; sec-ch-ua-platform https://cloud.51degrees.com; sec-ch-ua-platform-version https://cloud.51degrees.com"
45
+ />
46
+ </head>
47
+ <body>
48
+ <!-- ----------------------------------------------------------------------- -->
49
+ <!-- valid DOM elements -->
50
+ <div id="esc-key-handling-div"></div>
51
+ <div id="rc-panel-holder"></div>
52
+ <div id="root"></div>
53
+
54
+ <!-- Global error handling for script loading -->
55
+ <script>
56
+ (function () {
57
+ // Track failed scripts
58
+ window._failedScripts = [];
59
+
60
+ // Catch JavaScript errors
61
+ window.onerror = function (message, source, lineno, colno, error) {
62
+ console.error("[EasyEyes Error]", {
63
+ message: message,
64
+ source: source,
65
+ line: lineno,
66
+ column: colno,
67
+ error: error,
68
+ });
69
+ return false; // Let default handler run too
70
+ };
71
+
72
+ // Catch unhandled promise rejections
73
+ window.onunhandledrejection = function (event) {
74
+ console.error("[EasyEyes Unhandled Promise Rejection]", event.reason);
75
+ };
76
+
77
+ // Listen for script/resource load errors
78
+ window.addEventListener(
79
+ "error",
80
+ function (event) {
81
+ if (event.target && event.target.tagName === "SCRIPT") {
82
+ const src = event.target.src || "unknown";
83
+ window._failedScripts.push(src);
84
+ console.error("[EasyEyes Script Load Failed]", src);
85
+ }
86
+ },
87
+ true,
88
+ ); // Use capture phase to catch errors before they bubble
89
+
90
+ // Log ready state changes
91
+ document.addEventListener("readystatechange", function () {
92
+ console.log("[EasyEyes] document.readyState:", document.readyState);
93
+ if (document.readyState === "complete") {
94
+ if (window._failedScripts.length > 0) {
95
+ console.error(
96
+ "[EasyEyes] The following scripts failed to load:",
97
+ window._failedScripts,
98
+ );
99
+ } else {
100
+ console.log("[EasyEyes] All scripts loaded successfully.");
101
+ }
102
+ }
103
+ });
104
+ })();
105
+ </script>
106
+
107
+ <!-- external libraries -->
108
+ <script src="js/experimentLanguage.js"></script>
109
+ <script src="https://cdn.jsdelivr.net/npm/remote-calibrator@0.8.881/lib/RemoteCalibrator.min.js"></script>
110
+ <script
111
+ src="https://cdn.jsdelivr.net/npm/speaker-calibration@2.2.269/dist/main.js"
112
+ crossorigin="anonymous"
113
+ ></script>
114
+ <!-- <script src="https://www.keypad.website/ExperimentPeer.js"></script> -->
115
+ <!-- <script src="ExperimentPeer.js"></script> -->
116
+ <script
117
+ id="virtual-keypad-peer"
118
+ src="https://cdn.jsdelivr.net/gh/EasyEyes/virtual-keypad/dist/ExperimentPeer.js"
119
+ ></script>
120
+ <!-- <script src="main.js" ></script> -->
121
+ <script
122
+ crossorigin
123
+ src="https://cloud.51degrees.com/api/v4/AQSjtocC5XcfFwKc20g.js"
124
+ id="51DegreesScript"
125
+ ></script>
126
+ <script src="https://cdn.jsdelivr.net/npm/marked@4.0.7/marked.min.js"></script>
127
+ <!-- PsychoJS originals -->
128
+ <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
129
+ <script src="https://cdn.jsdelivr.net/npm/jquery-ui-dist@1.12.1/jquery-ui.min.js"></script>
130
+ <script src="https://cdn.jsdelivr.net/npm/preloadjs@1.0.1/lib/preloadjs.min.js"></script>
131
+ <!-- -------------------------------- TEMP --------------------------------- -->
132
+ <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
133
+
134
+ <!-- compatibility-check -->
135
+ <script
136
+ src="https://peer.easyeyes.app/main.js"
137
+ type="module"
138
+ crossorigin
139
+ ></script>
140
+
141
+ <script
142
+ src="https://connection-manager-14ac1ef82705.herokuapp.com/main.js"
143
+ type="module"
144
+ crossorigin
145
+ ></script>
146
+
147
+ <!-- initial load -->
148
+ <script type="module" src="js/first.min.js"></script>
149
+ <!-- experiment -->
150
+ <script type="module" src="js/threshold.min.js" defer></script>
151
+ </body>
152
+ </html>
package/index.html ADDED
@@ -0,0 +1,153 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
6
+
7
+ <!-- Load serviceWorker as soon as possible -->
8
+ <!-- We explicity enable COOP/COEP to use ffmpeg.wasm -->
9
+ <!-- https://dev.to/stefnotch/enabling-coop-coep-without-touching-the-server-2d3n -->
10
+ <!-- https://github.com/gzuidhof/coi-serviceworker -->
11
+ <script src="coi-serviceworker.js"></script>
12
+
13
+ <script
14
+ src="https://js.sentry-cdn.com/8d5c414335e8ff6ebf585b7204830e5f.min.js"
15
+ crossorigin="anonymous"
16
+ data-lazy="no"
17
+ ></script>
18
+
19
+ <title>EasyEyes Study</title>
20
+ <link rel="icon" type="image/x-icon" href="components/images/favicon.ico" />
21
+
22
+ <!-- styles -->
23
+ <link
24
+ rel="stylesheet"
25
+ href="https://cdn.jsdelivr.net/npm/jquery-ui-dist@1.12.1/jquery-ui.min.css"
26
+ />
27
+
28
+ <link
29
+ rel="stylesheet"
30
+ href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
31
+ integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
32
+ crossorigin="anonymous"
33
+ />
34
+ <link
35
+ rel="stylesheet"
36
+ href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.6.1/font/bootstrap-icons.css"
37
+ />
38
+ <script
39
+ src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js"
40
+ integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF"
41
+ crossorigin="anonymous"
42
+ ></script>
43
+ <meta
44
+ http-equiv="Delegate-CH"
45
+ content="sec-ch-ua-full-version-list https://cloud.51degrees.com; sec-ch-ua-model https://cloud.51degrees.com; sec-ch-ua-platform https://cloud.51degrees.com; sec-ch-ua-platform-version https://cloud.51degrees.com"
46
+ />
47
+ </head>
48
+ <body>
49
+ <!-- ----------------------------------------------------------------------- -->
50
+ <!-- valid DOM elements -->
51
+ <div id="esc-key-handling-div"></div>
52
+ <div id="rc-panel-holder"></div>
53
+ <div id="root"></div>
54
+
55
+ <!-- Global error handling for script loading -->
56
+ <script>
57
+ (function () {
58
+ // Track failed scripts
59
+ window._failedScripts = [];
60
+
61
+ // Catch JavaScript errors
62
+ window.onerror = function (message, source, lineno, colno, error) {
63
+ console.error("[EasyEyes Error]", {
64
+ message: message,
65
+ source: source,
66
+ line: lineno,
67
+ column: colno,
68
+ error: error,
69
+ });
70
+ return false; // Let default handler run too
71
+ };
72
+
73
+ // Catch unhandled promise rejections
74
+ window.onunhandledrejection = function (event) {
75
+ console.error("[EasyEyes Unhandled Promise Rejection]", event.reason);
76
+ };
77
+
78
+ // Listen for script/resource load errors
79
+ window.addEventListener(
80
+ "error",
81
+ function (event) {
82
+ if (event.target && event.target.tagName === "SCRIPT") {
83
+ const src = event.target.src || "unknown";
84
+ window._failedScripts.push(src);
85
+ console.error("[EasyEyes Script Load Failed]", src);
86
+ }
87
+ },
88
+ true,
89
+ ); // Use capture phase to catch errors before they bubble
90
+
91
+ // Log ready state changes
92
+ document.addEventListener("readystatechange", function () {
93
+ console.log("[EasyEyes] document.readyState:", document.readyState);
94
+ if (document.readyState === "complete") {
95
+ if (window._failedScripts.length > 0) {
96
+ console.error(
97
+ "[EasyEyes] The following scripts failed to load:",
98
+ window._failedScripts,
99
+ );
100
+ } else {
101
+ console.log("[EasyEyes] All scripts loaded successfully.");
102
+ }
103
+ }
104
+ });
105
+ })();
106
+ </script>
107
+
108
+ <!-- external libraries -->
109
+ <script src="js/experimentLanguage.js"></script>
110
+ <script src="https://cdn.jsdelivr.net/npm/remote-calibrator@0.9.161"></script>
111
+ <script
112
+ src="https://cdn.jsdelivr.net/npm/speaker-calibration@2.2.272/dist/main.js"
113
+ crossorigin="anonymous"
114
+ ></script>
115
+ <!-- <script src="https://www.keypad.website/ExperimentPeer.js"></script> -->
116
+ <!-- <script src="ExperimentPeer.js"></script> -->
117
+ <script
118
+ id="virtual-keypad-peer"
119
+ src="https://cdn.jsdelivr.net/gh/EasyEyes/virtual-keypad/dist/ExperimentPeer.js"
120
+ ></script>
121
+ <!-- <script src="main.js" ></script> -->
122
+ <script
123
+ crossorigin
124
+ src="https://cloud.51degrees.com/api/v4/AQSjtocC5XcfFwKc20g.js"
125
+ id="51DegreesScript"
126
+ ></script>
127
+ <script src="https://cdn.jsdelivr.net/npm/marked@4.0.7/marked.min.js"></script>
128
+ <!-- PsychoJS originals -->
129
+ <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
130
+ <script src="https://cdn.jsdelivr.net/npm/jquery-ui-dist@1.12.1/jquery-ui.min.js"></script>
131
+ <script src="https://cdn.jsdelivr.net/npm/preloadjs@1.0.1/lib/preloadjs.min.js"></script>
132
+ <!-- -------------------------------- TEMP --------------------------------- -->
133
+ <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
134
+
135
+ <!-- compatibility-check -->
136
+ <script
137
+ src="https://peer.easyeyes.app/main.js"
138
+ type="module"
139
+ crossorigin
140
+ ></script>
141
+
142
+ <script
143
+ src="https://connection-manager-14ac1ef82705.herokuapp.com/main.js"
144
+ type="module"
145
+ crossorigin
146
+ ></script>
147
+
148
+ <!-- initial load -->
149
+ <script type="module" src="js/first.min.js"></script>
150
+ <!-- experiment -->
151
+ <script type="module" src="js/threshold.min.js" defer></script>
152
+ </body>
153
+ </html>