Brain Hacks Lab
Run 3 real psychology experiments on yourself in the browser.
Introduction
30 Second Summary
Every day, your brain takes shortcuts to make quick decisions. Advertisers, politicians, and product designers know exactly how to exploit those shortcuts to influence what you buy, how you vote, and what you choose.
In this project, you will build an interactive web app that secretly runs three real psychology experiments on whoever uses it, then reveals exactly how their brain was manipulated.
What You'll Build
A single-page web app called "Brain Hacks Lab" that disguises itself as a decision-making quiz while secretly running three classic cognitive bias experiments on the user.
By the end of this project, you'll have:
- A welcome screen that frames the experience as a harmless "decision-making assessment" to disguise the real experiments.
- Three working psychology experiments (anchoring effect, framing effect, and choice overload) that manipulate decisions in real time.
- A results reveal screen that shows personalized data proving how each bias affected the user's brain, alongside the original research findings.
- Secret Mission: Add a 4th experiment, the Stroop Effect, where the user's brain fights itself trying to name ink colors that spell different color words.
Are there any prerequisites?
None. No prior coding experience is required. All code is provided step-by-step, and the entire project runs locally in your web browser with no installs beyond VS Code.
Set Up Your Project Files
Every web app starts with a basic file structure. Your Brain Hacks Lab needs three files that work together: HTML for structure, CSS for styling, and JavaScript for interactivity.
In this step, you'll create your project folder and files, then connect them with a basic HTML boilerplate that links everything together.
In this step, get ready to:
- Create a brain-hacks-lab folder on your Desktop.
- Create three project files: index.html, style.css, and script.js.
- Set up HTML boilerplate that links all three files together.
Create your project folder
You need a dedicated folder to keep all your Brain Hacks Lab files organized in one place. You'll create this folder on your Desktop so it's easy to find.
- In VS Code, click Terminal in the top menu bar.
- Click New Terminal.
What is a terminal?
A terminal is a text-based way to give instructions to your computer. Instead of clicking through folders, you type short commands to create files and folders instantly.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir brain-hacks-lab
cd brain-hacks-lab
What do these commands do?
- mkdir brain-hacks-lab creates a new folder called brain-hacks-lab.
- cd brain-hacks-lab moves you inside that folder so everything you create next goes there.
- Confirm the folder exists by running:
ls
You should see no output (the folder is empty). That's expected.
Seeing an error about the directory?
If you see a message about the directory not being found, make sure you typed cd ~/Desktop first (with the capital D in Desktop).
If mkdir says the folder already exists, that's fine. Just run cd brain-hacks-lab to move into the existing folder.
Help me troubleshoot my terminal commands.
Open the folder in VS Code and create your files
Now you'll open your new folder as a workspace in VS Code. This tells VS Code to treat brain-hacks-lab as your project home.
- In VS Code, click File in the top menu bar.
- Click Open Folder.
- Navigate to your Desktop, select the brain-hacks-lab folder, then click Open.
VS Code's left sidebar now shows the Explorer panel with your empty brain-hacks-lab folder.
- Hover over the folder name in the Explorer panel and click the New File icon (it looks like a page with a plus sign).
- Type index.html and press Enter.
- Click the New File icon again and create a file called style.css.
- Click the New File icon one more time and create a file called script.js.
Your Explorer panel should now show three files: index.html, style.css, and script.js.
Don't see the New File icon?
Make sure the Explorer panel is visible in the left sidebar. If you don't see it, press Cmd+Shift+E (macOS) or Ctrl+Shift+E (Windows) to open it.
The New File icons appear when you hover over the folder name area at the top of the Explorer panel.
Help me find the New File button in VS Code.
Write the HTML boilerplate
HTML boilerplate is the standard starter code that every web page needs. It tells the browser what kind of file this is and links your CSS and JavaScript files so they all work together.
- Click on index.html in the Explorer to open it in the editor.
- Paste the following code into the file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Brain Hacks Lab</title>
<!-- Link to the CSS stylesheet for visual styling -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Link to the JavaScript file for interactivity -->
<script src="script.js"></script>
</body>
</html>
What does this code do?
- <!DOCTYPE html> tells the browser this is a modern HTML5 page.
- The <head> section contains metadata that the browser reads but doesn't display on the page.
- <link rel="stylesheet" href="style.css"> connects your CSS file so styles apply to the page.
- <script src="script.js"></script> connects your JavaScript file so interactive behavior loads when the page opens.
- Save index.html by pressing Cmd+S (macOS) or Ctrl+S (Windows).
- Open the brain-hacks-lab folder on your Desktop using Finder (macOS) or File Explorer (Windows).
- Double-click index.html to open it in your web browser.
You should see a completely blank white page. That's correct. There's no visible content yet because the <body> is empty.
- Open the browser console by pressing Cmd+Option+J (macOS Chrome) or Ctrl+Shift+J (Windows Chrome).
- Confirm there are no red error messages in the console.
Seeing a red error in the console?
If you see an error mentioning a file not being found, check that style.css and script.js exist in the same folder as index.html. All three files must be in the brain-hacks-lab folder together.
Also check that the filenames in your HTML match exactly. The href must say style.css (not styles.css) and the src must say script.js (not scripts.js).
Help me fix the errors in my browser console.
✔️ Awesome, I've got everything!
Great. Make sure you've saved index.html and you're ready to move on.
ⓧ I'd like to double check the full code
Here's the complete index.html file for comparison:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Brain Hacks Lab</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<script src="script.js"></script>
</body>
</html>
The style.css and script.js files should both be empty at this point.
Your project skeleton is ready. Next up, you'll build the welcome screen that lures your user into the experiment without revealing what's really happening.
Build the Welcome Screen and App Structure
Your files are ready. Now you need to build the skeleton of the entire app so that each experiment lives in its own hidden screen.
The trick that makes psychology experiments work is deception. If you tell someone "I'm about to test whether a random number changes your guess," they'll resist. So your welcome screen will frame this as a neutral "decision-making assessment." The user won't know they're being experimented on until the reveal at the end.
In this step, get ready to:
- Create the HTML structure with hidden sections for each experiment screen.
- Style the app with a dark laboratory theme and smooth transitions.
- Add a screen navigation function that switches between experiments.
Create the HTML structure with hidden sections
The app uses a single HTML page with five <section> elements. Only one section is visible at a time. The others stay hidden until JavaScript reveals them.
- In VS Code, click on index.html in the Explorer sidebar to open it.
- Select all the existing code and replace it with the following:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Brain Hacks Lab</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<section id="welcome" class="screen active">
<h1>Decision-Making Assessment</h1>
<p>This short assessment measures how you make decisions under different conditions. There are no right or wrong answers.</p>
<p>It takes about 2 minutes.</p>
<button onclick="startExperiment()">Begin Assessment</button>
</section>
<section id="anchoring" class="screen">
<h2>Question 1</h2>
<p class="anchor-display">Reference number: <span id="anchor-number"></span></p>
<p>What percentage of United Nations member countries are in Africa?</p>
<input type="number" id="anchor-estimate" min="0" max="100" placeholder="Enter your estimate (0-100)">
<button onclick="submitAnchoring()">Next</button>
</section>
<section id="framing" class="screen">
<h2>Question 2</h2>
<p id="framing-scenario"></p>
<div class="options">
<button class="option-btn" onclick="submitFraming('certain')">
<span id="option-a-text"></span>
</button>
<button class="option-btn" onclick="submitFraming('gamble')">
<span id="option-b-text"></span>
</button>
</div>
</section>
<section id="choice-overload" class="screen">
<h2 id="choice-heading">Pick your favorite color</h2>
<p id="choice-instructions">Click the one that appeals to you most.</p>
<div id="color-grid" class="color-grid"></div>
</section>
<section id="results" class="screen">
<h1>Your Brain Got Hacked</h1>
<p class="results-intro">This was not a decision-making assessment. It was three psychology experiments running on your brain in real time. Here is what happened:</p>
<div class="result-card">
<h3>Experiment 1: The Anchoring Effect</h3>
<p id="anchoring-result"></p>
<p class="science">The science: Tversky and Kahneman (1974) showed random numbers pull estimates toward them. Groups shown 10 guessed 25% on average. Groups shown 65 guessed 45%. The actual answer is about 28%.</p>
</div>
<div class="result-card">
<h3>Experiment 2: The Framing Effect</h3>
<p id="framing-result"></p>
<p class="science">The science: Tversky and Kahneman (1981) found that when the same scenario is framed as "saving lives," 72% choose the safe option. When framed as "people dying," 78% choose the gamble. Same facts, opposite choices.</p>
</div>
<div class="result-card">
<h3>Experiment 3: Choice Overload</h3>
<p id="choice-result"></p>
<p class="science">The science: Iyengar and Lepper (2000) found that people shown 6 options were more likely to choose and more satisfied than those shown 24-30 options. More choice often leads to slower decisions and decision fatigue.</p>
</div>
<div class="result-card takeaway">
<h3>The Takeaway</h3>
<p>Knowing about cognitive biases does not make you immune to them. Your brain uses shortcuts (heuristics) that are fast and usually good enough, but they can be systematically exploited. Advertisers, politicians, and product designers use these exact principles every day.</p>
</div>
<button onclick="location.reload()">Run It Again</button>
</section>
</div>
<script src="script.js"></script>
</body>
</html>
What does this HTML do?
Each <section> is one screen of the app. Only the section with class active is visible at any time. The CSS will hide the rest.
- welcome is the deceptive opening that frames everything as a neutral assessment.
- anchoring plants a random number in the user's head before asking an estimation question.
- framing presents the same scenario with different wording to flip the user's risk preference.
- choice-overload measures how having too many options slows decision-making.
- results reveals the manipulation and explains the science behind each experiment.
Why is the welcome screen deliberately vague?
Real psychology experiments rely on deception. If you tell someone "I'm about to anchor your estimate with a random number," the effect disappears. By calling it a "Decision-Making Assessment," you keep the user unaware that they're being experimented on. That's what makes the reveal at the end so powerful.
- Save index.html.
- Open index.html in your browser by double-clicking the file in Finder (macOS) or File Explorer (Windows). You should see unstyled text that says "Decision-Making Assessment" with a button below it.
Seeing a blank page?
- Make sure you saved the file after pasting. Press Cmd+S (macOS) or Ctrl+S (Windows).
- Check that you replaced ALL the old code, including the <!DOCTYPE html> line at the top.
- Open the browser console (Cmd+Option+J on macOS or Ctrl+Shift+J on Windows) and check for red error messages.
Help me debug why my index.html is showing a blank page.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on.
ⓧ I'd like to double check the full code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Brain Hacks Lab</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<section id="welcome" class="screen active">
<h1>Decision-Making Assessment</h1>
<p>This short assessment measures how you make decisions under different conditions. There are no right or wrong answers.</p>
<p>It takes about 2 minutes.</p>
<button onclick="startExperiment()">Begin Assessment</button>
</section>
<section id="anchoring" class="screen">
<h2>Question 1</h2>
<p class="anchor-display">Reference number: <span id="anchor-number"></span></p>
<p>What percentage of United Nations member countries are in Africa?</p>
<input type="number" id="anchor-estimate" min="0" max="100" placeholder="Enter your estimate (0-100)">
<button onclick="submitAnchoring()">Next</button>
</section>
<section id="framing" class="screen">
<h2>Question 2</h2>
<p id="framing-scenario"></p>
<div class="options">
<button class="option-btn" onclick="submitFraming('certain')">
<span id="option-a-text"></span>
</button>
<button class="option-btn" onclick="submitFraming('gamble')">
<span id="option-b-text"></span>
</button>
</div>
</section>
<section id="choice-overload" class="screen">
<h2 id="choice-heading">Pick your favorite color</h2>
<p id="choice-instructions">Click the one that appeals to you most.</p>
<div id="color-grid" class="color-grid"></div>
</section>
<section id="results" class="screen">
<h1>Your Brain Got Hacked</h1>
<p class="results-intro">This was not a decision-making assessment. It was three psychology experiments running on your brain in real time. Here is what happened:</p>
<div class="result-card">
<h3>Experiment 1: The Anchoring Effect</h3>
<p id="anchoring-result"></p>
<p class="science">The science: Tversky and Kahneman (1974) showed random numbers pull estimates toward them. Groups shown 10 guessed 25% on average. Groups shown 65 guessed 45%. The actual answer is about 28%.</p>
</div>
<div class="result-card">
<h3>Experiment 2: The Framing Effect</h3>
<p id="framing-result"></p>
<p class="science">The science: Tversky and Kahneman (1981) found that when the same scenario is framed as "saving lives," 72% choose the safe option. When framed as "people dying," 78% choose the gamble. Same facts, opposite choices.</p>
</div>
<div class="result-card">
<h3>Experiment 3: Choice Overload</h3>
<p id="choice-result"></p>
<p class="science">The science: Iyengar and Lepper (2000) found that people shown 6 options were more likely to choose and more satisfied than those shown 24-30 options. More choice often leads to slower decisions and decision fatigue.</p>
</div>
<div class="result-card takeaway">
<h3>The Takeaway</h3>
<p>Knowing about cognitive biases does not make you immune to them. Your brain uses shortcuts (heuristics) that are fast and usually good enough, but they can be systematically exploited. Advertisers, politicians, and product designers use these exact principles every day.</p>
</div>
<button onclick="location.reload()">Run It Again</button>
</section>
</div>
<script src="script.js"></script>
</body>
</html>
Style the app with a dark laboratory theme
The dark color scheme gives the app a "lab" feeling and keeps focus on the content. The CSS also controls which screen is visible using display: none on hidden screens and display: block on the active one.
- Click on style.css in the Explorer sidebar.
- Add the base layout and typography styles by pasting the following:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background-color: #1a1a2e;
color: #e0e0e0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
max-width: 640px;
width: 90%;
padding: 2rem;
}
.screen {
display: none;
animation: fadeIn 0.4s ease-in;
}
.screen.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
h1 {
font-size: 2rem;
margin-bottom: 1rem;
color: #ffffff;
}
h2 {
font-size: 1.5rem;
margin-bottom: 1rem;
color: #ffffff;
}
p {
font-size: 1.1rem;
line-height: 1.6;
margin-bottom: 1rem;
}
button {
background-color: #4a90d9;
color: #ffffff;
border: none;
padding: 0.8rem 1.6rem;
font-size: 1rem;
border-radius: 6px;
cursor: pointer;
margin-top: 1rem;
transition: background-color 0.2s;
}
button:hover {
background-color: #357abd;
}
input[type="number"] {
background-color: #2a2a4a;
border: 2px solid #4a90d9;
color: #ffffff;
padding: 0.8rem;
font-size: 1.2rem;
border-radius: 6px;
width: 200px;
display: block;
margin-bottom: 1rem;
}
input[type="number"]::placeholder {
color: #888;
}
What do these styles do?
- The .screen class hides all sections by default. Only the section with .active shows up.
- The fadeIn animation makes each new screen slide gently into view instead of appearing instantly.
- The dark background (#1a1a2e) with light text (#e0e0e0) gives it a clinical, focused laboratory feel.
- Save style.css and refresh your browser.
- You should see a dark page with white "Decision-Making Assessment" text and a blue "Begin Assessment" button centered on the screen.
Page still looks unstyled?
- Check that your index.html file has <link rel="stylesheet" href="style.css"> in the <head> section.
- Make sure style.css is saved in the same folder as index.html. Both must be inside brain-hacks-lab.
Help me figure out why my CSS is not loading.
- Now add the experiment-specific component styles below the code you just pasted in style.css:
.anchor-display {
font-size: 1.4rem;
color: #4a90d9;
font-weight: bold;
padding: 1rem;
background-color: #2a2a4a;
border-radius: 6px;
text-align: center;
}
.options {
display: flex;
flex-direction: column;
gap: 1rem;
margin-top: 1rem;
}
.option-btn {
background-color: #2a2a4a;
border: 2px solid #4a4a6a;
padding: 1.2rem;
text-align: left;
font-size: 1rem;
line-height: 1.4;
}
.option-btn:hover {
border-color: #4a90d9;
background-color: #3a3a5a;
}
.color-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(60px, 1fr));
gap: 0.5rem;
margin-top: 1rem;
}
.color-swatch {
width: 100%;
aspect-ratio: 1;
border-radius: 8px;
cursor: pointer;
border: 3px solid transparent;
transition: transform 0.15s, border-color 0.15s;
}
.color-swatch:hover {
transform: scale(1.1);
border-color: #ffffff;
}
.result-card {
background-color: #2a2a4a;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.result-card h3 {
color: #4a90d9;
margin-bottom: 0.5rem;
}
.science {
font-size: 0.9rem;
color: #aaa;
font-style: italic;
border-top: 1px solid #3a3a5a;
padding-top: 0.75rem;
margin-top: 0.75rem;
}
.takeaway {
border: 2px solid #4a90d9;
}
.results-intro {
font-size: 1.1rem;
margin-bottom: 1.5rem;
color: #ccc;
}
#results h1 {
color: #ff6b6b;
}
What are these component styles for?
- .anchor-display highlights the random anchor number in a blue box so it catches the user's eye (key to making the anchoring effect work).
- .color-grid and .color-swatch lay out the color choices in a responsive grid for the choice overload experiment.
- .result-card creates distinct cards for each experiment reveal. The .takeaway card gets a highlighted border because it's the final conclusion.
- Save style.css and refresh your browser.
- The welcome screen should still look the same (these new styles apply to screens you haven't navigated to yet).
Seeing a CSS syntax error in the console?
- Make sure there is no missing closing brace } between the first block of styles and the second. Each selector block must have matching opening and closing braces.
- Check that you pasted the second block AFTER the first block, not inside it.
Help me find the CSS error in my style.css file.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on.
ⓧ I'd like to double check the full code
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background-color: #1a1a2e;
color: #e0e0e0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
max-width: 640px;
width: 90%;
padding: 2rem;
}
.screen {
display: none;
animation: fadeIn 0.4s ease-in;
}
.screen.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
h1 {
font-size: 2rem;
margin-bottom: 1rem;
color: #ffffff;
}
h2 {
font-size: 1.5rem;
margin-bottom: 1rem;
color: #ffffff;
}
p {
font-size: 1.1rem;
line-height: 1.6;
margin-bottom: 1rem;
}
button {
background-color: #4a90d9;
color: #ffffff;
border: none;
padding: 0.8rem 1.6rem;
font-size: 1rem;
border-radius: 6px;
cursor: pointer;
margin-top: 1rem;
transition: background-color 0.2s;
}
button:hover {
background-color: #357abd;
}
input[type="number"] {
background-color: #2a2a4a;
border: 2px solid #4a90d9;
color: #ffffff;
padding: 0.8rem;
font-size: 1.2rem;
border-radius: 6px;
width: 200px;
display: block;
margin-bottom: 1rem;
}
input[type="number"]::placeholder {
color: #888;
}
.anchor-display {
font-size: 1.4rem;
color: #4a90d9;
font-weight: bold;
padding: 1rem;
background-color: #2a2a4a;
border-radius: 6px;
text-align: center;
}
.options {
display: flex;
flex-direction: column;
gap: 1rem;
margin-top: 1rem;
}
.option-btn {
background-color: #2a2a4a;
border: 2px solid #4a4a6a;
padding: 1.2rem;
text-align: left;
font-size: 1rem;
line-height: 1.4;
}
.option-btn:hover {
border-color: #4a90d9;
background-color: #3a3a5a;
}
.color-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(60px, 1fr));
gap: 0.5rem;
margin-top: 1rem;
}
.color-swatch {
width: 100%;
aspect-ratio: 1;
border-radius: 8px;
cursor: pointer;
border: 3px solid transparent;
transition: transform 0.15s, border-color 0.15s;
}
.color-swatch:hover {
transform: scale(1.1);
border-color: #ffffff;
}
.result-card {
background-color: #2a2a4a;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.result-card h3 {
color: #4a90d9;
margin-bottom: 0.5rem;
}
.science {
font-size: 0.9rem;
color: #aaa;
font-style: italic;
border-top: 1px solid #3a3a5a;
padding-top: 0.75rem;
margin-top: 0.75rem;
}
.takeaway {
border: 2px solid #4a90d9;
}
.results-intro {
font-size: 1.1rem;
margin-bottom: 1.5rem;
color: #ccc;
}
#results h1 {
color: #ff6b6b;
}
Add the screen navigation function
The app needs a way to switch between screens. This one function handles all navigation by removing active from every screen and adding it to the one you want to show.
- Click on script.js in the Explorer sidebar.
- Add the screen navigation function by pasting the following:
function showScreen(id) {
document.querySelectorAll('.screen').forEach(function(screen) {
screen.classList.remove('active');
});
document.getElementById(id).classList.add('active');
}
What does this function do?
When called, showScreen('anchoring') first hides ALL sections by stripping the active class from each one. Then it finds the section with the matching ID and makes it visible by adding active back. The CSS fadeIn animation triggers automatically when a screen becomes visible.
- Save script.js and refresh your browser.
- You won't see a visible change yet. The Begin Assessment button calls startExperiment() which doesn't exist yet. Clicking it will show an error in the console. That's expected.
Seeing an error when you click the button?
If you click Begin Assessment and see an error in the console about startExperiment not being defined, that's completely normal. You'll build that function in the next step.
Help me check that my script.js is loading correctly.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on.
ⓧ I'd like to double check the full code
function showScreen(id) {
document.querySelectorAll('.screen').forEach(function(screen) {
screen.classList.remove('active');
});
document.getElementById(id).classList.add('active');
}
Your lab is set up and the deception is in place. Next, you'll build the first experiment that plants a number in the user's head and watches it pull their estimate in that direction.
Build the Anchoring Experiment
Your welcome screen looks professional and deceptive. The user thinks they are taking a quick decision-making assessment. Now it is time to build the first experiment that will secretly manipulate their thinking.
The anchoring effect is one of the most powerful cognitive biases ever discovered. When people see a random number before making an estimate, that number "anchors" their thinking and pulls their answer toward it. You will now build this experiment into your app.
In this step, get ready to:
- Write the JavaScript that randomly assigns the user to a high or low anchor number.
- Store the user's anchor group and their estimate for the results reveal later.
- Connect the Next button so it advances to the framing experiment.
Create the data storage for all experiments
Before writing any experiment logic, you need a place to store each user's responses. This data object will hold results from all three experiments so the reveal screen can display everything at the end.
- In VS Code, switch back to script.js.
- At the very top of the file, above the existing showScreen function, add this results object and two variables:
let results = {
anchoring: { anchor: null, estimate: null },
framing: { frame: null, choice: null },
choice: { timeFew: null, timeMany: null }
};
let choiceStartTime = null;
let choiceRound = 1;
What does this code do?
The results object is your experiment notebook. It has a section for each of the three experiments: anchoring, framing, and choice overload.
The choiceStartTime and choiceRound variables are for the choice overload experiment later. You are declaring them now so they are available when needed.
You won't see a change yet in the browser. The next chunk uses this data object to power the anchoring experiment.
Write the anchoring setup functions
The anchoring experiment works by showing the user a random "reference number" that is either very low (10) or very high (65). This number has nothing to do with the question, but it secretly pulls the user's estimate toward it.
- In script.js, scroll down below the closing } of the showScreen function.
- Add these two functions below it:
function startExperiment() {
setupAnchoring();
showScreen('anchoring');
}
function setupAnchoring() {
// Randomly assign user to high anchor (65) or low anchor (10)
let anchor = Math.random() < 0.5 ? 10 : 65;
results.anchoring.anchor = anchor;
document.getElementById('anchor-number').textContent = anchor;
}
What does this code do?
- startExperiment() is what the Begin Assessment button calls. It prepares the anchoring screen and then shows it.
- setupAnchoring() flips a coin. If Math.random() returns less than 0.5, the anchor is 10. Otherwise it is 65. This mirrors the original 1974 experiment by Tversky and Kahneman.
- The anchor value is stored in results.anchoring.anchor and displayed inside the #anchor-number span so the user sees it prominently on screen.
- Save script.js and refresh your browser.
- Click Begin Assessment. You should see the anchoring screen appear with either 10 or 65 displayed as the reference number.
- Refresh the page and click the button again a few times. The number should randomly switch between 10 and 65.
Button does nothing when clicked?
- Open your browser's developer console (press Cmd+Option+J on macOS or Ctrl+Shift+J on Windows) and look for red error messages.
- Make sure the results object is above the showScreen function, not below it. JavaScript reads top to bottom.
- Check that your index.html still has onclick="startExperiment()" on the Begin Assessment button.
Help me debug why my Begin Assessment button is not working.
Write the submission handler
The user can now see the anchor number and the estimation question. You need a function that grabs their answer, validates it, stores it for the reveal, and advances them to the next experiment.
- In script.js, add this function below setupAnchoring:
function submitAnchoring() {
// Get the user's estimate from the input field
let estimate = document.getElementById('anchor-estimate').value;
// Validate the input is a number between 0 and 100
if (estimate === '' || estimate < 0 || estimate > 100) {
alert('Please enter a number between 0 and 100.');
return;
}
// Store the estimate and advance to the framing experiment
results.anchoring.estimate = parseInt(estimate);
setupFraming();
showScreen('framing');
}
What does this code do?
- It reads the value from the #anchor-estimate input field.
- The if block prevents the user from leaving the field empty or entering something outside 0-100.
- parseInt(estimate) converts the text input into a number and saves it to the results object.
- It calls setupFraming() (which you will build in the next step) and navigates to the framing screen.
- Save script.js and refresh your browser.
- Click Begin Assessment, then enter a number (like 30) in the estimate field.
- Click Next. The screen should advance to a blank "Question 2" page. This confirms your anchoring data was stored and the app moved forward.
Why does the console show an error about setupFraming?
You may see an error in the console mentioning that setupFraming is not defined. This is expected. You will build that function in the next step. The screen still advances because showScreen('framing') runs on its own line after the error.
Clicking Next does nothing?
- Make sure you typed a number in the input field first. The validation blocks empty submissions.
- Check that your function is named exactly submitAnchoring to match the onclick in the HTML.
- Open the console and check for syntax errors. A missing bracket or quote will prevent the entire file from loading.
Help me figure out why my Next button is not advancing to the next screen.
Why is this experiment so effective?
In the original 1974 study, participants who saw the number 10 estimated around 25% on average. Participants who saw 65 estimated around 45%. The actual answer is about 28% (54 out of 193 UN countries are in Africa).
The random number has zero relevance to the question, yet it shifts people's answers by 20 percentage points. Your app now does the same thing to every user who takes the assessment.
✔️ Awesome, I've got everything!
Great. Double check you have saved script.js before moving on.
ⓧ I'd like to double check the full code
Here is the complete script.js file as it should look at the end of this step:
let results = {
anchoring: { anchor: null, estimate: null },
framing: { frame: null, choice: null },
choice: { timeFew: null, timeMany: null }
};
let choiceStartTime = null;
let choiceRound = 1;
function showScreen(id) {
document.querySelectorAll('.screen').forEach(function(screen) {
screen.classList.remove('active');
});
document.getElementById(id).classList.add('active');
}
function startExperiment() {
setupAnchoring();
showScreen('anchoring');
}
function setupAnchoring() {
let anchor = Math.random() < 0.5 ? 10 : 65;
results.anchoring.anchor = anchor;
document.getElementById('anchor-number').textContent = anchor;
}
function submitAnchoring() {
let estimate = document.getElementById('anchor-estimate').value;
if (estimate === '' || estimate < 0 || estimate > 100) {
alert('Please enter a number between 0 and 100.');
return;
}
results.anchoring.estimate = parseInt(estimate);
setupFraming();
showScreen('framing');
}
Your first experiment is live. Every user now gets secretly assigned to either a high or low anchor group, and their estimate gets recorded for the big reveal. Next up, you will build the framing experiment that makes the same facts trigger opposite decisions.
Build the Framing Experiment
The anchoring experiment planted a number in the user's head to pull their estimate. Now you will build something even sneakier.
The framing effect shows that presenting identical facts with different wording causes people to make opposite decisions. You will present the exact same life-or-death scenario using either "gain" language or "loss" language, then watch how the wording alone flips the user's risk preference.
In this step, get ready to:
- Randomly assign the user to a gain frame or loss frame.
- Display two program options and record the user's choice.
- Advance to the choice overload experiment on selection.
Write the setupFraming function
This function randomly assigns the user to one of two conditions. In the "gain" frame, the options talk about saving lives. In the "loss" frame, the options talk about people dying. The facts are mathematically identical, but the emotional weight is completely different.
- In script.js, add the setupFraming() function below your submitAnchoring() function by copying this code:
function setupFraming() {
// Randomly assign to gain or loss frame
let isGainFrame = Math.random() < 0.5;
results.framing.frame = isGainFrame ? 'gain' : 'loss';
// Set the scenario description
let scenario = 'Imagine a deadly disease is expected to kill 600 people. Two programs have been proposed:';
document.getElementById('framing-scenario').textContent = scenario;
// Display options based on the assigned frame
if (isGainFrame) {
document.getElementById('option-a-text').textContent =
'Program A: 200 people will be saved.';
document.getElementById('option-b-text').textContent =
'Program B: There is a 1/3 chance that 600 people will be saved, and a 2/3 chance that nobody will be saved.';
} else {
document.getElementById('option-a-text').textContent =
'Program A: 400 people will die.';
document.getElementById('option-b-text').textContent =
'Program B: There is a 1/3 chance that nobody will die, and a 2/3 chance that 600 people will die.';
}
}
What does this code do?
This function uses the same coin-flip technique as the anchoring experiment to split users into two groups.
- Math.random() < 0.5 randomly assigns the user to either the gain frame or the loss frame.
- The gain frame says "200 people will be saved" (positive language). The loss frame says "400 people will die" (negative language). These are mathematically identical outcomes for 600 people.
- Program A is the certain outcome. Program B is the risky gamble. Research shows the wording alone flips which one people pick.
Why is this the same scenario with different words?
"200 saved out of 600" and "400 will die out of 600" describe the exact same mathematical outcome. Tversky and Kahneman (1981) discovered that when framed as saving lives, 72% of people choose the safe option. When framed as people dying, 78% choose the risky gamble.
Your app is replicating this classic "Asian Disease Problem" on the user in real time.
You won't see a change yet because this function is already called by submitAnchoring() from the previous step. The next chunk will make the buttons work.
Record the user's choice and advance to the next experiment
When the user clicks one of the two program buttons, you need to store whether they picked the certain option or the risky gamble, then move them to the next experiment.
- Add the submitFraming() function below your setupFraming() function by copying this code:
function submitFraming(choice) {
// Store whether user picked 'certain' or 'gamble'
results.framing.choice = choice;
// Move to the next experiment
setupChoiceOverload();
showScreen('choice-overload');
}
What does this code do?
- The choice parameter comes from the HTML buttons. Program A passes 'certain' and Program B passes 'gamble'.
- setupChoiceOverload() does not exist yet. You will build it in the next step. For now, clicking a button will cause a console error, but the choice still gets stored.
- Save script.js.
- Refresh index.html in your browser. Click Begin Assessment, enter any number, and click Next.
- You should see the framing screen with a disease scenario and two program options. Refresh a few times to confirm you see different wording ("saved" vs "will die").
Not seeing the framing scenario?
- Make sure you saved script.js after adding both functions.
- Check that the function is named exactly setupFraming (capital F). The call in submitAnchoring() uses this exact name.
- Open your browser console (press F12) and look for red error messages that point to the problem.
Why isn't my framing experiment showing the scenario text?
✔️ Awesome, I've got everything!
Great. Double check you have saved script.js and the framing screen displays correctly.
ⓧ I'd like to double check the full code
Here is the complete script.js file at this point:
let results = {
anchoring: { anchor: null, estimate: null },
framing: { frame: null, choice: null },
choice: { timeFew: null, timeMany: null }
};
let choiceStartTime = null;
let choiceRound = 1;
function showScreen(id) {
document.querySelectorAll('.screen').forEach(function(screen) {
screen.classList.remove('active');
});
document.getElementById(id).classList.add('active');
}
function startExperiment() {
setupAnchoring();
showScreen('anchoring');
}
function setupAnchoring() {
let anchor = Math.random() < 0.5 ? 10 : 65;
results.anchoring.anchor = anchor;
document.getElementById('anchor-number').textContent = anchor;
}
function submitAnchoring() {
let estimate = document.getElementById('anchor-estimate').value;
if (estimate === '' || estimate < 0 || estimate > 100) {
alert('Please enter a number between 0 and 100.');
return;
}
results.anchoring.estimate = parseInt(estimate);
setupFraming();
showScreen('framing');
}
function setupFraming() {
let isGainFrame = Math.random() < 0.5;
results.framing.frame = isGainFrame ? 'gain' : 'loss';
let scenario = 'Imagine a deadly disease is expected to kill 600 people. Two programs have been proposed:';
document.getElementById('framing-scenario').textContent = scenario;
if (isGainFrame) {
document.getElementById('option-a-text').textContent =
'Program A: 200 people will be saved.';
document.getElementById('option-b-text').textContent =
'Program B: There is a 1/3 chance that 600 people will be saved, and a 2/3 chance that nobody will be saved.';
} else {
document.getElementById('option-a-text').textContent =
'Program A: 400 people will die.';
document.getElementById('option-b-text').textContent =
'Program B: There is a 1/3 chance that nobody will die, and a 2/3 chance that 600 people will die.';
}
}
function submitFraming(choice) {
results.framing.choice = choice;
setupChoiceOverload();
showScreen('choice-overload');
}
Your framing experiment is live. The user has no idea they are being manipulated. They just see a straightforward question about a disease outbreak. Next, you will overwhelm them with choices and measure how it slows their brain down.
Build the Choice Overload Experiment and Results Reveal
Your first two experiments are manipulating the user's decisions behind the scenes. Now it is time for the grand finale.
The choice overload experiment tests whether having more options actually helps people decide. You will show 6 color swatches, time how fast the user picks, then show 24 swatches and time that choice too. After that, the results reveal drops the curtain on all three experiments at once.
In this step, get ready to:
- Build the two-round color swatch experiment that times each decision.
- Build the results reveal that exposes how each experiment hacked the user's brain.
- Run the complete app from start to finish.
Build the color palette generator
The choice overload experiment needs a palette of colors to show the user. You will create a function that returns a specific number of colors from a predefined list of 24.
- In script.js, find the closing brace of the submitFraming function at the bottom of your file.
- Add the following function below it:
function generateColors(count) {
let colors = [
'#e74c3c', '#e91e63', '#9b59b6', '#673ab7',
'#3498db', '#03a9f4', '#00bcd4', '#009688',
'#27ae60', '#4caf50', '#8bc34a', '#cddc39',
'#f1c40f', '#ff9800', '#ff5722', '#795548',
'#607d8b', '#f06292', '#7986cb', '#4dd0e1',
'#81c784', '#aed581', '#dce775', '#fff176'
];
return colors.slice(0, count);
}
What does this code do?
This function stores a palette of 24 distinct hex color values. When called with a number (like 6 or 24), it returns only that many colors from the list using slice().
This lets you show 6 colors in round 1 and all 24 in round 2, using the same function both times.
- Save script.js.
- You will not see a change yet. The next function uses this palette to build the color grid.
Build the choice overload experiment
Now you need two functions. The first builds the color grid on screen and starts a timer. The second records how long the user took and either moves to round 2 or finishes the experiment.
- In script.js, add the following function below generateColors:
function setupChoiceOverload() {
let grid = document.getElementById('color-grid');
grid.innerHTML = '';
let colors;
if (choiceRound === 1) {
colors = generateColors(6);
document.getElementById('choice-heading').textContent = 'Round 1: Pick your favorite color';
} else {
colors = generateColors(24);
document.getElementById('choice-heading').textContent = 'Round 2: Pick your favorite color';
}
colors.forEach(function(color) {
let swatch = document.createElement('div');
swatch.className = 'color-swatch';
swatch.style.backgroundColor = color;
swatch.onclick = function() { submitChoice(); };
grid.appendChild(swatch);
});
choiceStartTime = Date.now();
}
What does this code do?
This function checks which round the user is on. In round 1, it shows 6 swatches. In round 2, it shows all 24.
For each color, it creates a div element with the color-swatch class (already styled in your CSS) and attaches a click handler. The last line, choiceStartTime = Date.now(), starts the timer the moment the grid appears.
- Now add the submitChoice function directly below setupChoiceOverload:
function submitChoice() {
let elapsed = Date.now() - choiceStartTime;
if (choiceRound === 1) {
results.choice.timeFew = elapsed;
choiceRound = 2;
setupChoiceOverload();
} else {
results.choice.timeMany = elapsed;
showResults();
}
}
What does this code do?
When the user clicks any swatch, this calculates how many milliseconds passed since the grid appeared. In round 1, it stores that time in results.choice.timeFew and immediately rebuilds the grid with 24 colors for round 2.
In round 2, it stores the time in results.choice.timeMany and calls showResults() (which you will write next).
- Save script.js and refresh your browser.
- Click through the welcome screen, enter an estimate, select a framing option, and you should now see 6 color swatches on the choice overload screen.
- Clicking a swatch should reload the grid with 24 swatches for round 2.
Not seeing the color swatches?
- Open your browser console (press Cmd+Option+J on macOS or Ctrl+Shift+J on Windows) and check for red error messages.
- Make sure generateColors is defined above setupChoiceOverload in your file. JavaScript needs functions to be defined before they are called in this pattern.
- Verify that your HTML still has a div with id="color-grid" inside the choice-overload section.
Help me debug my choice overload experiment
Build the results reveal
This is the payoff. The user thinks they just took a decision-making quiz. Now you reveal that every screen was a psychology experiment running on their brain in real time.
- In script.js, add the showResults function below submitChoice:
function showResults() {
// Build the anchoring result text based on which anchor was shown
let anchorText;
if (results.anchoring.anchor === 65) {
anchorText = 'You were shown the number 65 as a "reference." Your estimate was ' +
results.anchoring.estimate + '%. ';
if (results.anchoring.estimate > 30) {
anchorText += 'The high anchor pulled your estimate upward. It worked.';
} else {
anchorText += 'Interesting - you resisted the pull of the high anchor. Most people do not.';
}
} else {
anchorText = 'You were shown the number 10 as a "reference." Your estimate was ' +
results.anchoring.estimate + '%. ';
if (results.anchoring.estimate < 25) {
anchorText += 'The low anchor pulled your estimate downward. It worked.';
} else {
anchorText += 'Interesting - you resisted the pull of the low anchor. Most people do not.';
}
}
document.getElementById('anchoring-result').textContent = anchorText;
// Build the framing result text based on gain or loss frame
let framingText;
if (results.framing.frame === 'gain') {
framingText = 'You were shown the GAIN frame ("people will be saved"). You chose the ' +
(results.framing.choice === 'certain' ? 'safe option' : 'risky gamble') + '. ';
if (results.framing.choice === 'certain') {
framingText += 'This is exactly what the framing effect predicts: gain frames make people risk-averse.';
} else {
framingText += 'You bucked the trend. Most people in the gain frame choose the safe option.';
}
} else {
framingText = 'You were shown the LOSS frame ("people will die"). You chose the ' +
(results.framing.choice === 'certain' ? 'safe option' : 'risky gamble') + '. ';
if (results.framing.choice === 'gamble') {
framingText += 'This is exactly what the framing effect predicts: loss frames make people risk-seeking.';
} else {
framingText += 'You bucked the trend. Most people in the loss frame choose the risky gamble.';
}
}
document.getElementById('framing-result').textContent = framingText;
// Build the choice overload result comparing the two round times
let timeFew = (results.choice.timeFew / 1000).toFixed(1);
let timeMany = (results.choice.timeMany / 1000).toFixed(1);
let choiceText = 'With 6 colors, you decided in ' + timeFew + ' seconds. With 24 colors, you took ' +
timeMany + ' seconds. ';
if (results.choice.timeMany > results.choice.timeFew) {
let ratio = (results.choice.timeMany / results.choice.timeFew).toFixed(1);
choiceText += 'That is ' + ratio + 'x longer. More options did not help you. They slowed you down.';
} else {
choiceText += 'Unusually, you were faster with more options. Most people slow down significantly when overwhelmed with choice.';
}
document.getElementById('choice-result').textContent = choiceText;
showScreen('results');
}
What does this code do?
This function builds personalized result text for each experiment using the data stored throughout the session.
- For anchoring, it checks whether the user saw 10 or 65, then evaluates whether their estimate was pulled toward the anchor.
- For framing, it reveals which frame was shown and whether the user's risk preference matched the predicted pattern.
- For choice overload, it converts milliseconds to seconds, compares the two round times, and calculates how much slower the user was with more options.
Each result is injected into the HTML elements you set up earlier. The final showScreen('results') call reveals the entire reveal page at once.
- Save script.js and refresh your browser.
- Run through the entire app: click Begin Assessment, enter an estimate, select a framing option, pick a color in round 1, pick a color in round 2.
- You should land on the results reveal screen with a red "Your Brain Got Hacked" heading and three experiment cards showing your personal data.
What is the Run It Again button?
The Run It Again button at the bottom of the results page calls location.reload() to refresh the entire page. This resets all variables and randomizes the experiment conditions again.
This is how you share it with friends. Send them the file and let them run through it themselves to see their own biases in action.
Results page is blank or shows errors?
- Check the browser console for errors. A common issue is a typo in one of the element IDs like anchoring-result, framing-result, or choice-result.
- If the choice times show NaN, make sure choiceStartTime = Date.now() is the last line inside setupChoiceOverload.
- If clicking a swatch does nothing in round 2, confirm that submitChoice calls showResults() in the else branch.
Help me debug my results reveal page
✔️ Awesome, I've got everything!
Great. Double check you have saved script.js and confirmed the full app runs without errors.
ⓧ I'd like to double check the full code
Here is the complete script.js file at this point:
let results = {
anchoring: { anchor: null, estimate: null },
framing: { frame: null, choice: null },
choice: { timeFew: null, timeMany: null }
};
let choiceStartTime = null;
let choiceRound = 1;
function showScreen(id) {
document.querySelectorAll('.screen').forEach(function(screen) {
screen.classList.remove('active');
});
document.getElementById(id).classList.add('active');
}
function startExperiment() {
setupAnchoring();
showScreen('anchoring');
}
function setupAnchoring() {
let anchor = Math.random() < 0.5 ? 10 : 65;
results.anchoring.anchor = anchor;
document.getElementById('anchor-number').textContent = anchor;
}
function submitAnchoring() {
let estimate = document.getElementById('anchor-estimate').value;
if (estimate === '' || estimate < 0 || estimate > 100) {
alert('Please enter a number between 0 and 100.');
return;
}
results.anchoring.estimate = parseInt(estimate);
setupFraming();
showScreen('framing');
}
function setupFraming() {
let isGainFrame = Math.random() < 0.5;
results.framing.frame = isGainFrame ? 'gain' : 'loss';
let scenario = 'Imagine a deadly disease is expected to kill 600 people. Two programs have been proposed:';
document.getElementById('framing-scenario').textContent = scenario;
if (isGainFrame) {
document.getElementById('option-a-text').textContent =
'Program A: 200 people will be saved.';
document.getElementById('option-b-text').textContent =
'Program B: There is a 1/3 chance that 600 people will be saved, and a 2/3 chance that nobody will be saved.';
} else {
document.getElementById('option-a-text').textContent =
'Program A: 400 people will die.';
document.getElementById('option-b-text').textContent =
'Program B: There is a 1/3 chance that nobody will die, and a 2/3 chance that 600 people will die.';
}
}
function submitFraming(choice) {
results.framing.choice = choice;
setupChoiceOverload();
showScreen('choice-overload');
}
function generateColors(count) {
let colors = [
'#e74c3c', '#e91e63', '#9b59b6', '#673ab7',
'#3498db', '#03a9f4', '#00bcd4', '#009688',
'#27ae60', '#4caf50', '#8bc34a', '#cddc39',
'#f1c40f', '#ff9800', '#ff5722', '#795548',
'#607d8b', '#f06292', '#7986cb', '#4dd0e1',
'#81c784', '#aed581', '#dce775', '#fff176'
];
return colors.slice(0, count);
}
function setupChoiceOverload() {
let grid = document.getElementById('color-grid');
grid.innerHTML = '';
let colors;
if (choiceRound === 1) {
colors = generateColors(6);
document.getElementById('choice-heading').textContent = 'Round 1: Pick your favorite color';
} else {
colors = generateColors(24);
document.getElementById('choice-heading').textContent = 'Round 2: Pick your favorite color';
}
colors.forEach(function(color) {
let swatch = document.createElement('div');
swatch.className = 'color-swatch';
swatch.style.backgroundColor = color;
swatch.onclick = function() { submitChoice(); };
grid.appendChild(swatch);
});
choiceStartTime = Date.now();
}
function submitChoice() {
let elapsed = Date.now() - choiceStartTime;
if (choiceRound === 1) {
results.choice.timeFew = elapsed;
choiceRound = 2;
setupChoiceOverload();
} else {
results.choice.timeMany = elapsed;
showResults();
}
}
function showResults() {
let anchorText;
if (results.anchoring.anchor === 65) {
anchorText = 'You were shown the number 65 as a "reference." Your estimate was ' +
results.anchoring.estimate + '%. ';
if (results.anchoring.estimate > 30) {
anchorText += 'The high anchor pulled your estimate upward. It worked.';
} else {
anchorText += 'Interesting - you resisted the pull of the high anchor. Most people do not.';
}
} else {
anchorText = 'You were shown the number 10 as a "reference." Your estimate was ' +
results.anchoring.estimate + '%. ';
if (results.anchoring.estimate < 25) {
anchorText += 'The low anchor pulled your estimate downward. It worked.';
} else {
anchorText += 'Interesting - you resisted the pull of the low anchor. Most people do not.';
}
}
document.getElementById('anchoring-result').textContent = anchorText;
let framingText;
if (results.framing.frame === 'gain') {
framingText = 'You were shown the GAIN frame ("people will be saved"). You chose the ' +
(results.framing.choice === 'certain' ? 'safe option' : 'risky gamble') + '. ';
if (results.framing.choice === 'certain') {
framingText += 'This is exactly what the framing effect predicts: gain frames make people risk-averse.';
} else {
framingText += 'You bucked the trend. Most people in the gain frame choose the safe option.';
}
} else {
framingText = 'You were shown the LOSS frame ("people will die"). You chose the ' +
(results.framing.choice === 'certain' ? 'safe option' : 'risky gamble') + '. ';
if (results.framing.choice === 'gamble') {
framingText += 'This is exactly what the framing effect predicts: loss frames make people risk-seeking.';
} else {
framingText += 'You bucked the trend. Most people in the loss frame choose the risky gamble.';
}
}
document.getElementById('framing-result').textContent = framingText;
let timeFew = (results.choice.timeFew / 1000).toFixed(1);
let timeMany = (results.choice.timeMany / 1000).toFixed(1);
let choiceText = 'With 6 colors, you decided in ' + timeFew + ' seconds. With 24 colors, you took ' +
timeMany + ' seconds. ';
if (results.choice.timeMany > results.choice.timeFew) {
let ratio = (results.choice.timeMany / results.choice.timeFew).toFixed(1);
choiceText += 'That is ' + ratio + 'x longer. More options did not help you. They slowed you down.';
} else {
choiceText += 'Unusually, you were faster with more options. Most people slow down significantly when overwhelmed with choice.';
}
document.getElementById('choice-result').textContent = choiceText;
showScreen('results');
}
Secret mission
Add the Stroop Effect Experiment
Your lab currently runs three experiments. But there is a fourth cognitive bias that is even more visceral than the others. The Stroop effect forces your brain to fight itself in real time. Can you add it to the lab and watch users struggle to override their own automatic responses?
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your project files or delete them.
Resources you used:
- The brain-hacks-lab folder on your Desktop (contains index.html, style.css, and script.js).
Keep everything running
No action needed. Choose this if you want to keep experimenting with the Brain Hacks Lab or share it with friends.
- Your brain-hacks-lab folder stays on your Desktop. You can open index.html in any browser anytime to run the experiments again.
Pause - I'll come back to this later
Since this project is just local files with no running processes, there is nothing to pause. Your files will be waiting for you whenever you come back.
- Close the VS Code window for the brain-hacks-lab workspace.
- Close the browser tab if you have index.html open.
Delete - I don't want to use this again
Remove all project files from your computer.
- Close VS Code if the brain-hacks-lab folder is open.
- Delete the project folder by running the command for your operating system below.
macOS
rm -rf ~/Desktop/brain-hacks-lab
Windows
rmdir /s /q %USERPROFILE%\Desktop\brain-hacks-lab
- Confirm the folder is gone by running:
ls ~/Desktop
You should no longer see brain-hacks-lab in the output.
Nice Work!
Nice Work!
You just built a fully interactive psychology lab that runs real cognitive bias experiments on anyone who opens it. Your Brain Hacks Lab proves that knowing about biases and actually resisting them are two very different things.
You learned how to:
- Build a multi-screen interactive web app using vanilla HTML, CSS, and JavaScript that runs real cognitive psychology experiments in the browser.
- Experience the anchoring effect and framing effect firsthand, replicating Tversky and Kahneman's classic findings that random numbers pull estimates and different wording flips risk preferences.
- Experience choice overload by comparing your own decision speed across 6 vs 24 options, replicating Iyengar and Lepper's 2000 jam study.
- Secret Mission: experience the Stroop effect, where automatic word-reading interfered with color identification, demonstrating the conflict between fast (System 1) and slow (System 2) cognitive processes.
Ready to quiz yourself?