diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 020d55031f1d78efc29fd39501635359263e4d6e..c4f6de98797b8dda368c9de896b58e9d342ad3ea 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -2,7 +2,8 @@
 
 <nav>
 	<ul>
+		<li><a href="./mips-data-dependency">Data Dependencies (🚧)</a></li>
 		<li><a href="./lru">LRU</a></li>
-		<li><a href="./scoreboard">Scoreboard</a></li>
+		<li><a href="./scoreboard">Scoreboard (🚧</a></li>
 	</ul>
 </nav>
diff --git a/src/routes/mips-data-dependency/+page.svelte b/src/routes/mips-data-dependency/+page.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..cfd91ae416610d453deda46a9aa990a6dd20090f
--- /dev/null
+++ b/src/routes/mips-data-dependency/+page.svelte
@@ -0,0 +1,215 @@
+<script>
+	import { generateInstructions, hasWarHazard } from './hazards';
+
+	import Operand from './operand.svelte';
+
+	/** @type {{instruction: string, operands: string[]}[]} */
+	let hazardousInstructions = [];
+
+	/** @type {[number,number][]} */
+	let currentlyMarked = [];
+	$: notTwoSelected = currentlyMarked.length !== 2;
+
+	/** @type {string[]} */
+	let hazardsFound = [];
+
+	/** @type {[number,number][]} */
+	let colorings = [];
+
+	/**
+	 * @param instructionIndex {number}
+	 * @param operandIndex {number}
+	 */
+	function handleOpClick(instructionIndex, operandIndex) {
+		if (currentlyMarked.some(([li, oi]) => li === instructionIndex && oi === operandIndex)) {
+			currentlyMarked = currentlyMarked.filter(
+				([li, oi]) => !(li === instructionIndex && oi === operandIndex)
+			);
+		} else {
+			if (currentlyMarked.length >= 2) {
+				return;
+			}
+			currentlyMarked = [...currentlyMarked, [instructionIndex, operandIndex]];
+		}
+		hazardousInstructions = hazardousInstructions;
+	}
+
+	function handleWarClick() {
+		const [firstMarker, secondMarker] = currentlyMarked.sort((a, b) => a[0] - b[0]);
+		const firstInstruction = hazardousInstructions[firstMarker[0]];
+		const secondInstruction = hazardousInstructions[secondMarker[0]];
+		const isWar = hasWarHazard(
+			firstInstruction,
+			firstMarker[1],
+			secondInstruction,
+			secondMarker[1]
+		);
+
+		if (isWar) {
+			currentlyMarked = [];
+
+			hazardsFound = [
+				...hazardsFound,
+				'WAR: ' +
+					stringifyInstruction(firstInstruction) +
+					' -> ' +
+					stringifyInstruction(secondInstruction)
+			];
+			colorings = [...colorings, firstMarker, secondMarker];
+
+			hazardousInstructions = hazardousInstructions;
+		} else {
+			alert('Computer says: No WAR');
+		}
+	}
+
+	function handleRawClick() {}
+
+	function handleWawClick() {}
+
+	/**
+	 * @param instruction {{
+	 *   instruction: string;
+	 *   operands: string[];
+	 *  }}
+	 */
+	function stringifyInstruction(instruction) {
+		return `${instruction.instruction}  ${instruction.operands.join(',')}`;
+	}
+
+	/**
+	 * @param instrIndex {number}
+	 * @param operandIndex {number}
+	 */
+	function getOperandColor(instrIndex, operandIndex) {
+		if (colorings.some(([ii, oi]) => ii === instrIndex && oi === operandIndex)) {
+			return 'red';
+		} else {
+			return null;
+		}
+	}
+</script>
+
+<h1>Data Dependencies</h1>
+
+<fieldset>
+	<details>
+		<summary>What is this all about?</summary>
+		<p>
+			While theoretically, CPU instructions follow each other back-to-back, one per clock cycle, in
+			reality varying instructions tend take a varying amount of time to complete.
+			<br />
+			Consider this code:
+		</p>
+		<pre>ADD.D  F3;F1;F2   ; F3 = F1+F2<br />ADD.D  F4;F1;F3   ; F4 = F1+F3</pre>
+		<p>
+			The second instruction uses the result of the first instruction (<code>F3</code>) as one if
+			its operands. To be able to do so, the first <code>ADD.D</code> instruction must have finished
+			writing it's result into register <code>F3</code>. If the second instruction is executed
+			before the first finished, it may use an old value of <code>F3</code>, resulting in a wrong
+			result.
+		</p>
+		<p>
+			This kind of dependency is called <em>RAW</em> (Read After Write). If not considered, it can
+			become a <em>Data Hazard</em>. To avoid that, CPUs employ <em>Scoreboarding</em> or
+			<em>Tomasulos Algorithm</em>.
+		</p>
+
+		<hr />
+		<pre>ADD.D  F3;F1;F2   ; F3 = F1+F2<br />ADD.D  F2;F5;F6   ; F2 = F5+F6</pre>
+		<p>
+			In this example, the second instruction writes to <code>F2</code>, which is an operand for the
+			first instruction. If executed without care, this write might happen before the first
+			instruction had time to read it first, again resulting in incorrect calculation. A
+			<em>WAR</em> (Write After Read) hazard occured.
+		</p>
+
+		<hr />
+
+		<pre>MUL.D  F3;F1;F2   ; F3 = F1+F2<br />ADD.D  F3;F5;F6   ; F3 = F5+F6</pre>
+		<p>
+			The last data hazard to mention is <em>WAW</em> (Write After Write). If instructions are
+			executed out of order, instruction two may write to <code>F3</code> before the first one. As a
+			result, the first instructions writes its result last, leaving the final output wrong.
+		</p>
+	</details>
+</fieldset>
+<br />
+
+<div class="quiz">
+	<p>1. Mark two operands between which a data hazard exists:</p>
+	{#each hazardousInstructions as hazardInstr, lineIndex}
+		<p>
+			<code class="line-number">{lineIndex + 1}</code>
+			<code style="instruction-line">
+				{hazardInstr.instruction}
+				<span class="operands-list">
+					{#each hazardInstr.operands as operand, operandIndex}
+						<Operand
+							marked={currentlyMarked.some(([li, oi]) => li === lineIndex && oi === operandIndex)}
+							op={operand}
+							color={getOperandColor(lineIndex, operandIndex)}
+							on:clicked={() => handleOpClick(lineIndex, operandIndex)}
+						/>
+						<span>,</span>
+					{/each}
+				</span>
+			</code>
+		</p>
+	{:else}
+		<button
+			style:width="16rem"
+			style:height="13rem"
+			on:click={() => (hazardousInstructions = generateInstructions())}
+		>
+			Generate exercise
+		</button>
+	{/each}
+
+	<p>2. Select which hazard it is exactly:</p>
+
+	<div class="button-group">
+		<button disabled={true} on:click={handleRawClick}>RAW</button>
+		<button disabled={notTwoSelected} on:click={handleWarClick}>WAR</button>
+		<button disabled={true} on:click={handleWawClick}>WAW</button>
+	</div>
+	<p>3. Repeat, until there are no more data hazards</p>
+	<ul>
+		{#each hazardsFound as hazard}
+			<li>
+				{hazard}
+			</li>
+		{/each}
+	</ul>
+</div>
+
+<style>
+	code.line-number {
+		color: gray;
+		margin-right: 0.5rem;
+		padding-right: 0.5rem;
+		border-right: solid 1px gray;
+	}
+	div.quiz code {
+		font-size: x-large;
+	}
+	div.quiz button {
+		font-size: large;
+		width: 5rem;
+		height: 2rem;
+	}
+	div.button-group {
+		display: flex;
+		flex-direction: row;
+		gap: 0.5rem;
+	}
+	span.operands-list {
+		margin-left: 1rem;
+		display: inline-flex;
+		gap: 0.5rem;
+	}
+	/* Hide the last , */
+	span.operands-list span:last-child {
+		display: none;
+	}
+</style>
diff --git a/src/routes/mips-data-dependency/hazards.js b/src/routes/mips-data-dependency/hazards.js
new file mode 100644
index 0000000000000000000000000000000000000000..c38b5b89028e543471b4b72adb808c5d9a29a75b
--- /dev/null
+++ b/src/routes/mips-data-dependency/hazards.js
@@ -0,0 +1,31 @@
+const possibleInstructions = ['ADD.D', 'MUL.D'];
+const possibleRegisters = ['F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8'];
+
+/** @param arr {string[]} */
+function randomFrom(arr) {
+	const randomIndex = Math.floor(Math.random() * arr.length);
+	return arr[randomIndex];
+}
+
+/**
+ * @returns {{instruction: string, operands: string[]}[]}
+ */
+export function generateInstructions() {
+	return new Array(5).fill(null).map(() => ({
+		instruction: randomFrom(possibleInstructions),
+		operands: new Array(3).fill(null).map(() => randomFrom(possibleRegisters))
+	}));
+}
+
+/**
+ *
+ * @param {{instruction: string, operands: string[]}} instr1
+ * @param {number} selectedOp1
+ * @param {{instruction: string, operands: string[]}} instr2
+ * @param {number} selectedOp2
+ * @returns {boolean}
+ */
+export function hasWarHazard(instr1, selectedOp1, instr2, selectedOp2) {
+	const registerWrittenTo = instr2.operands[selectedOp2];
+	return instr1.operands[1] === registerWrittenTo || instr1.operands[2] === registerWrittenTo;
+}
diff --git a/src/routes/mips-data-dependency/operand.svelte b/src/routes/mips-data-dependency/operand.svelte
new file mode 100644
index 0000000000000000000000000000000000000000..e674bd1c22e389b46a5e1760543742284f677f88
--- /dev/null
+++ b/src/routes/mips-data-dependency/operand.svelte
@@ -0,0 +1,34 @@
+<script>
+	import { createEventDispatcher } from 'svelte';
+	const dispatch = createEventDispatcher();
+
+	/** @type {string} */
+	export let op;
+	/** @type {boolean} */
+	export let marked = false;
+	/** @type {string | null} */
+	export let color = null;
+</script>
+
+<span style:color style:border-color={color} class:marked on:click={() => dispatch('clicked', op)}>
+	{op}
+</span>
+
+<style>
+	span {
+		border-width: 1px;
+		border-color: lightgray;
+		border-style: dashed;
+		padding-left: 0.4rem;
+		padding-right: 0.4rem;
+	}
+	span.marked {
+		font-weight: bold;
+		border-color: grey;
+	}
+	span:hover {
+		border-style: solid;
+		font-weight: bolder;
+		cursor: pointer;
+	}
+</style>