C.L.A.R.A. - Clear Lexical Analysis and Reasoning Aid Cabecera del proyecto C.L.A.R.A., parte del Proyecto WWW de Jorge Verón Schenone. CLARA Clear Lexical Analysis & Reasoning Aid

🏛️ Multilayer Perceptron — XOR Solver

Interactive demonstration of how a neural network solves the XOR problem

1. Select inputs

w=20 w=-20 w=-20 w=20 w=20 w=20 A 0 B 0 h1 0 h2 0 y 0% Input layer Hidden layer Output b1 = -10 b2 = -10 b3 = -30

📊 Real-time equations

h1 = ReLU(20·A + (-20)·B + (-10)) = ReLU(-10) = 0
h2 = ReLU((-20)·A + 20·B + (-10)) = ReLU(-10) = 0
y = σ(20·h1 + 20·h2 + (-30)) = σ(-30) = 0%
XOR(0, 0) = 0 ✓
Step 1: Calculate h1 activation (Specialist 1)
z1 = 20·0 + (-20)·0 + (-10) = -10.0
h1 = ReLU(z1) = max(0, -10.0) = 0.0
Step 2: Calculate h2 activation (Specialist 2)
z2 = (-20)·0 + 20·0 + (-10) = -10.0
h2 = ReLU(z2) = max(0, -10.0) = 0.0
Step 3: Calculate output y (Coordinator)
z3 = 20·0.0 + 20·0.0 + (-30) = -30.0
y = σ(z3) = 1 / (1 + e-(-30.0)) = 0.0000 = 0.0%
Since 0.0% < 50%, the verdict is 0.

📋 Dynamic truth table

ABExpected XORMLP predictsResult
000
011
101
110

🧠 Why does it work?

The Multilayer Perceptron solves XOR through a team of specialists:

Specialist 1 (h1): Detects (1,0)

Activates only when A=1 and B=0. With weights [20, -20] and bias -10:

• If A=1, B=0: 20·1 + (-20)·0 - 10 = 10 → ReLU(10) = 10 ✅

• If A=0, B=1: 20·0 + (-20)·1 - 10 = -30 → ReLU(-30) = 0 ❌

Specialist 2 (h2): Detects (0,1)

Activates only when A=0 and B=1. With weights [-20, 20] and bias -10:

• If A=0, B=1: (-20)·0 + 20·1 - 10 = 10 → ReLU(10) = 10 ✅

• If A=1, B=0: (-20)·1 + 20·0 - 10 = -30 → ReLU(-30) = 0 ❌

Coordinator (y): Sums the specialists

With weights [20, 20] and bias -30:

• If exactly ONE activates (XOR=1): 20·10 + 20·0 - 30 = 170 → σ(170) ≈ 100%

• If none activate (0,0): 20·0 + 20·0 - 30 = -30 → σ(-30) ≈ 0% ✅

📐 Why is XOR not linearly separable?

A B (0,0)→0 (1,1)→0 (0,1)→1 (1,0)→1 A single line? XOR = 1 XOR = 0

There is no single straight line that separates the red points (XOR=1) from the green ones (XOR=0).
That is why a Simple Perceptron (a single neuron) cannot solve XOR.
The MLP uses two hidden neurons to create two decision boundaries.

💾 Downloadable standalone code

Below is the complete source code of the Multilayer Perceptron standalone emulator, ready to copy, save, and run offline.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Multilayer Perceptron - XOR Solver | Proyecto WWW</title>
<!--
  ============================================================
  MULTILAYER PERCEPTRON (MLP) - XOR SOLVER - STANDALONE VERSION
  ============================================================
  Self-contained file for Proyecto WWW.
  
  Instructions:
    1. Copy this entire file.
    2. Paste it into a text editor (Notepad, VSCode, etc.).
    3. Save it as "perceptron-multilayer.html".
    4. Open it with any browser (Chrome, Firefox, Edge).
  
  No internet connection or software installation required.
  
  Author: Jorge Verón Schenone - Proyecto WWW
  https://www.proyectowww.com.ar/
  
  License: Creative Commons Attribution-ShareAlike 4.0 International
  https://creativecommons.org/licenses/by-sa/4.0/deed.en
  
  You are free to:
    - Share — copy and redistribute the material in any medium or format
    - Adapt — remix, transform, and build upon the material
    for any purpose, even commercially.
  
  Under the following terms:
    - Attribution — You must give appropriate credit, provide a link to the
      license, and indicate if changes were made.
    - ShareAlike — If you remix, transform, or build upon the material, you must
      distribute your contributions under the same license as the original.
  
  More resources at:
    - CLARA Prototype: https://proyectowww-it.blogspot.com/2026/06/claraclear.html
    - Full article: https://proyectowww-en.blogspot.com/2026/06/beliefs-in-transformation-rosenblatts.html
  ============================================================
-->
<style>
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: 'Georgia', serif;
  line-height: 1.6;
  color: #2c3e50;
  background: #f5f5f5;
  padding: 1.25rem;
  max-width: 62.5rem;
  margin: 0 auto;
}

/* ============================================
   HEADER WITH LINKS
   ============================================ */
.header-links {
  text-align: center;
  margin-bottom: 0.9375rem;
  font-size: 0.85em;
  color: #666;
}
.header-links a {
  color: #1e3c72;
  text-decoration: none;
  font-weight: bold;
}
.header-links a:hover {
  text-decoration: underline;
}
.header-links span {
  margin: 0 0.5rem;
  color: #ccc;
}

.container {
  background: white;
  padding: 1.875rem;
  border-radius: 0.5rem;
  box-shadow: 0 0.125rem 0.625rem rgba(0,0,0,0.1);
}

h1 {
  color: #1e3c72;
  text-align: center;
  margin-bottom: 0.625rem;
  font-size: 2em;
}

.subtitle {
  text-align: center;
  color: #666;
  font-style: italic;
  margin-bottom: 1.875rem;
}

/* Input selector */
.input-selector {
  background: #f8f9fa;
  padding: 1.25rem;
  border-radius: 0.5rem;
  margin-bottom: 1.5625rem;
  border: 0.125rem solid #e0e0e0;
}

.input-selector h3 {
  color: #1e3c72;
  margin-bottom: 0.9375rem;
  font-size: 1.2em;
}

.xor-buttons {
  display: flex;
  gap: 0.625rem;
  margin-bottom: 0.9375rem;
  flex-wrap: wrap;
}

.xor-btn {
  flex: 1;
  min-width: 5rem;
  padding: 0.75rem;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  border: none;
  border-radius: 0.375rem;
  cursor: pointer;
  font-size: 1em;
  font-weight: bold;
  transition: transform 0.2s, box-shadow 0.2s;
}

.xor-btn:hover {
  transform: translateY(-0.125rem);
  box-shadow: 0 0.25rem 0.75rem rgba(102, 126, 234, 0.4);
}

.xor-btn.active {
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}

.checkboxes {
  display: flex;
  gap: 1.875rem;
  align-items: center;
  padding: 0.9375rem;
  background: white;
  border-radius: 0.375rem;
}

.checkbox-group {
  display: flex;
  align-items: center;
  gap: 0.625rem;
}

.checkbox-group label {
  font-weight: bold;
  font-size: 1.1em;
  color: #1e3c72;
}

.checkbox-group input[type="checkbox"] {
  width: 1.5rem;
  height: 1.5rem;
  cursor: pointer;
}

/* Network SVG */
.network-container {
  background: #fafafa;
  padding: 1.25rem;
  border-radius: 0.5rem;
  margin-bottom: 1.5625rem;
  border: 0.125rem solid #e0e0e0;
}

svg {
  width: 100%;
  height: auto;
  display: block;
}

.neuron {
  transition: all 0.3s ease;
}

.neuron-circle {
  stroke: #333;
  stroke-width: 2;
  transition: fill 0.3s ease;
}

.neuron-label {
  font-family: 'Courier New', monospace;
  font-size: 1rem;
  font-weight: bold;
  text-anchor: middle;
  dominant-baseline: middle;
  pointer-events: none;
}

.neuron-value {
  font-family: 'Courier New', monospace;
  font-size: 0.875rem;
  text-anchor: middle;
  dominant-baseline: middle;
  fill: #666;
  pointer-events: none;
}

.connection {
  stroke-width: 3;
  transition: stroke 0.3s ease, stroke-width 0.3s ease;
}

.weight-label {
  font-family: 'Courier New', monospace;
  font-size: 0.75rem;
  fill: #333;
  cursor: help;
}

/* Equations panel */
.equations-panel {
  background: #e8f5e9;
  padding: 1.25rem;
  border-radius: 0.5rem;
  margin-bottom: 1.5625rem;
  border-left: 0.25rem solid #4caf50;
  font-family: 'Courier New', monospace;
}

.equations-panel h3 {
  color: #2e7d32;
  margin-bottom: 0.9375rem;
  font-family: 'Georgia', serif;
}

.equation-line {
  margin: 0.5rem 0;
  font-size: 0.95em;
  line-height: 1.8;
}

.equation-line strong {
  color: #1e3c72;
}

/* Result */
.result-box {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  padding: 1.25rem;
  border-radius: 0.5rem;
  text-align: center;
  margin-bottom: 1.5625rem;
  font-size: 1.3em;
  font-weight: bold;
}

.result-box.correct {
  background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
}

.result-box.incorrect {
  background: linear-gradient(135deg, #eb3349 0%, #f45c43 100%);
}

/* Full calculation toggle */
.calculation-toggle {
  width: 100%;
  padding: 0.9375rem;
  background: #f8f9fa;
  border: 0.125rem solid #dee2e6;
  border-radius: 0.5rem;
  cursor: pointer;
  font-size: 1em;
  font-weight: bold;
  color: #495057;
  transition: all 0.3s ease;
  margin-bottom: 0.625rem;
}

.calculation-toggle:hover {
  background: #e9ecef;
  border-color: #adb5bd;
}

.calculation-details {
  display: none;
  background: #fff3cd;
  padding: 1.25rem;
  border-radius: 0.5rem;
  border-left: 0.25rem solid #ffc107;
  margin-bottom: 1.5625rem;
  font-family: 'Courier New', monospace;
  font-size: 0.9em;
}

.calculation-details.visible {
  display: block;
}

.calc-step {
  margin: 0.625rem 0;
  padding: 0.625rem;
  background: white;
  border-radius: 0.25rem;
}

/* Truth table */
.truth-table-container {
  margin-bottom: 1.5625rem;
}

.truth-table-container h3 {
  color: #1e3c72;
  margin-bottom: 0.9375rem;
}

.truth-table {
  width: 100%;
  border-collapse: collapse;
  background: white;
  border-radius: 0.5rem;
  overflow: hidden;
  box-shadow: 0 0.125rem 0.5rem rgba(0,0,0,0.1);
}

.truth-table th {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  padding: 0.75rem;
  text-align: center;
  font-weight: bold;
}

.truth-table td {
  padding: 0.75rem;
  text-align: center;
  border-bottom: 0.0625rem solid #e0e0e0;
}

.truth-table tr:hover {
  background: #f8f9fa;
}

.truth-table .tested {
  background: #e8f5e9;
  font-weight: bold;
}

.truth-table .pending {
  color: #999;
  font-style: italic;
}

.check-mark {
  color: #4caf50;
  font-size: 1.3em;
}

.cross-mark {
  color: #f44336;
  font-size: 1.3em;
}

/* Explanation section */
.explanation-section {
  background: #f3e5f5;
  padding: 1.25rem;
  border-radius: 0.5rem;
  margin-bottom: 1.5625rem;
  border-left: 0.25rem solid #9c27b0;
}

.explanation-section h3 {
  color: #6a1b9a;
  margin-bottom: 0.9375rem;
}

.explanation-section p {
  margin: 0.625rem 0;
  line-height: 1.8;
}

.specialist-box {
  background: white;
  padding: 0.9375rem;
  border-radius: 0.375rem;
  margin: 0.9375rem 0;
  border-left: 0.1875rem solid #9c27b0;
}

.specialist-box h4 {
  color: #6a1b9a;
  margin-bottom: 0.625rem;
}

/* Nonlinear XOR diagram */
.nonlinear-diagram {
  background: #fff8e1;
  padding: 1.25rem;
  border-radius: 0.5rem;
  margin-bottom: 1.5625rem;
  border-left: 0.25rem solid #ff9800;
}

.nonlinear-diagram h3 {
  color: #e65100;
  margin-bottom: 0.9375rem;
}

/* Tooltips */
.tooltip-gen {
  position: relative;
  color: #d68910;
  text-decoration: none;
  font-weight: 500;
  border-bottom: 0.0625rem dotted #d68910;
  cursor: help;
}

.tooltip-gen:hover {
  color: #3498db;
  border-bottom: 0.0625rem solid #3498db;
}

.tooltip-gen:hover::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  background: #2c3e50;
  color: #ecf0f1;
  padding: 0.5rem 0.75rem;
  border-radius: 0.375rem;
  white-space: normal;
  z-index: 1000;
  font-size: 0.85em;
  max-width: 18.75rem;
  width: max-content;
  line-height: 1.4;
  box-shadow: 0 0.25rem 0.75rem rgba(0,0,0,0.3);
  font-weight: normal;
  text-align: left;
}

.tooltip-gen:hover::before {
  content: '';
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 0.375rem solid transparent;
  border-top-color: #2c3e50;
}

/* Instructions */
.instructions {
  background: #e3f2fd;
  padding: 1.25rem;
  border-radius: 0.5rem;
  border-left: 0.25rem solid #2196f3;
  margin-top: 1.875rem;
}

.instructions h3 {
  color: #1565c0;
  margin-bottom: 0.9375rem;
}

.instructions ol {
  margin-left: 1.25rem;
}

.instructions li {
  margin: 0.5rem 0;
}

/* License footer */
.license-footer {
  margin-top: 1.875rem;
  padding-top: 1.25rem;
  border-top: 0.0625rem solid #e0e0e0;
  font-size: 0.8em;
  color: #888;
  text-align: center;
  line-height: 1.6;
}

.license-footer a {
  color: #1e3c72;
  text-decoration: none;
}

.license-footer a:hover {
  text-decoration: underline;
}

/* Responsive */
@media (max-width: 48rem) {
  .container {
    padding: 0.9375rem;
  }
  
  h1 {
    font-size: 1.5em;
  }
  
  .xor-buttons {
    flex-direction: column;
  }
  
  .checkboxes {
    flex-direction: column;
    gap: 0.9375rem;
  }
  
  .equation-line {
    font-size: 0.85em;
  }
}
</style>
</head>
<body>

<div class="header-links">
  <a href="https://proyectowww-it.blogspot.com/2026/06/claraclear.html" target="_blank" rel="noopener">← CLARA Prototype</a>
  <span>|</span>
  <a href="https://www.proyectowww.com.ar/" target="_blank" rel="noopener">Proyecto WWW</a>
  <span>|</span>
  <a href="https://proyectowww-en.blogspot.com/2026/06/beliefs-in-transformation-rosenblatts.html" target="_blank" rel="noopener">Full article</a>
</div>

<div class="container">
  
  <h1>🏛️ Multilayer Perceptron — XOR Solver</h1>
  <p class="subtitle">Interactive demonstration of how a neural network solves the XOR problem</p>
  
  <!-- Input selector -->
  <div class="input-selector">
    <h3>1. Select inputs</h3>
    
    <div class="xor-buttons">
      <button class="xor-btn" onclick="setXOR(0,0)">0, 0 → 0</button>
      <button class="xor-btn" onclick="setXOR(0,1)">0, 1 → 1</button>
      <button class="xor-btn" onclick="setXOR(1,0)">1, 0 → 1</button>
      <button class="xor-btn" onclick="setXOR(1,1)">1, 1 → 0</button>
    </div>
    
    <div class="checkboxes">
      <div class="checkbox-group">
        <label>Input A:</label>
        <input type="checkbox" id="inputA" onchange="updateNetwork()">
      </div>
      <div class="checkbox-group">
        <label>Input B:</label>
        <input type="checkbox" id="inputB" onchange="updateNetwork()">
      </div>
    </div>
  </div>
  
  <!-- Neural network SVG -->
  <div class="network-container">
    <svg viewBox="0 0 800 400" xmlns="http://www.w3.org/2000/svg">
      <!-- Connections -->
      <line id="conn-A-h1" class="connection" x1="150" y1="150" x2="400" y2="150" stroke="#ccc"/>
      <line id="conn-A-h2" class="connection" x1="150" y1="150" x2="400" y2="250" stroke="#ccc"/>
      <line id="conn-B-h1" class="connection" x1="150" y1="250" x2="400" y2="150" stroke="#ccc"/>
      <line id="conn-B-h2" class="connection" x1="150" y1="250" x2="400" y2="250" stroke="#ccc"/>
      <line id="conn-h1-y" class="connection" x1="400" y1="150" x2="650" y2="200" stroke="#ccc"/>
      <line id="conn-h2-y" class="connection" x1="400" y1="250" x2="650" y2="200" stroke="#ccc"/>
      
      <!-- Weights with tooltips -->
      <text id="weight-A-h1" class="weight-label tooltip-gen" x="275" y="130" text-anchor="middle" data-tooltip="Weight from A to h1: 20. A positive value means when A=1, h1 tends to activate.">w=20</text>
      <text id="weight-A-h2" class="weight-label tooltip-gen" x="275" y="270" text-anchor="middle" data-tooltip="Weight from A to h2: -20. A negative value means when A=1, h2 tends to deactivate.">w=-20</text>
      <text id="weight-B-h1" class="weight-label tooltip-gen" x="275" y="170" text-anchor="middle" data-tooltip="Weight from B to h1: -20. A negative value means when B=1, h1 tends to deactivate.">w=-20</text>
      <text id="weight-B-h2" class="weight-label tooltip-gen" x="275" y="230" text-anchor="middle" data-tooltip="Weight from B to h2: 20. A positive value means when B=1, h2 tends to activate.">w=20</text>
      <text id="weight-h1-y" class="weight-label tooltip-gen" x="525" y="160" text-anchor="middle" data-tooltip="Weight from h1 to y: 20. Both specialists contribute with equal strength to the coordinator.">w=20</text>
      <text id="weight-h2-y" class="weight-label tooltip-gen" x="525" y="240" text-anchor="middle" data-tooltip="Weight from h2 to y: 20. Both specialists contribute with equal strength to the coordinator.">w=20</text>
      
      <!-- Input neurons -->
      <g class="neuron" id="neuron-A">
        <circle class="neuron-circle" cx="150" cy="150" r="35" fill="#e3f2fd"/>
        <text class="neuron-label" x="150" y="150">A</text>
        <text class="neuron-value" x="150" y="175" id="value-A">0</text>
      </g>
      
      <g class="neuron" id="neuron-B">
        <circle class="neuron-circle" cx="150" cy="250" r="35" fill="#e3f2fd"/>
        <text class="neuron-label" x="150" y="250">B</text>
        <text class="neuron-value" x="150" y="275" id="value-B">0</text>
      </g>
      
      <!-- Hidden neurons -->
      <g class="neuron" id="neuron-h1">
        <circle class="neuron-circle" cx="400" cy="150" r="35" fill="#fff3e0"/>
        <text class="neuron-label" x="400" y="150">h1</text>
        <text class="neuron-value" x="400" y="175" id="value-h1">0</text>
      </g>
      
      <g class="neuron" id="neuron-h2">
        <circle class="neuron-circle" cx="400" cy="250" r="35" fill="#fff3e0"/>
        <text class="neuron-label" x="400" y="250">h2</text>
        <text class="neuron-value" x="400" y="275" id="value-h2">0</text>
      </g>
      
      <!-- Output neuron -->
      <g class="neuron" id="neuron-y">
        <circle class="neuron-circle" cx="650" cy="200" r="40" fill="#fce4ec"/>
        <text class="neuron-label" x="650" y="200">y</text>
        <text class="neuron-value" x="650" y="225" id="value-y">0%</text>
      </g>
      
      <!-- Layer labels -->
      <text x="150" y="50" text-anchor="middle" font-size="14" fill="#666" font-weight="bold">Input layer</text>
      <text x="400" y="50" text-anchor="middle" font-size="14" fill="#666" font-weight="bold">Hidden layer</text>
      <text x="650" y="50" text-anchor="middle" font-size="14" fill="#666" font-weight="bold">Output</text>
      
      <!-- Biases with tooltips -->
      <text x="400" y="100" text-anchor="middle" font-size="12" fill="#999" class="tooltip-gen" data-tooltip="Bias of h1: -10. Threshold the neuron must exceed to activate.">b1 = -10</text>
      <text x="400" y="310" text-anchor="middle" font-size="12" fill="#999" class="tooltip-gen" data-tooltip="Bias of h2: -10. Threshold the neuron must exceed to activate.">b2 = -10</text>
      <text x="650" y="280" text-anchor="middle" font-size="12" fill="#999" class="tooltip-gen" data-tooltip="Bias of y: -30. Negative value that prevents the output from activating when no specialist does.">b3 = -30</text>
    </svg>
  </div>
  
  <!-- Equations panel -->
  <div class="equations-panel">
    <h3>📊 Real-time equations</h3>
    <div class="equation-line" id="eq-h1">
      <strong>h1</strong> = ReLU(20·A + (-20)·B + (-10)) = ReLU(<span id="calc-h1">-10</span>) = <span id="result-h1">0</span>
    </div>
    <div class="equation-line" id="eq-h2">
      <strong>h2</strong> = ReLU((-20)·A + 20·B + (-10)) = ReLU(<span id="calc-h2">-10</span>) = <span id="result-h2">0</span>
    </div>
    <div class="equation-line" id="eq-y">
      <strong>y</strong> = σ(20·h1 + 20·h2 + (-30)) = σ(<span id="calc-y">-30</span>) = <span id="result-y">0%</span>
    </div>
  </div>
  
  <!-- Result -->
  <div class="result-box" id="result-box">
    XOR(0, 0) = 0 ✓
  </div>
  
  <!-- Full calculation toggle -->
  <button class="calculation-toggle" onclick="toggleCalculation()">
    📝 Show full calculation [▼]
  </button>
  
  <div class="calculation-details" id="calculation-details">
    <div class="calc-step">
      <strong>Step 1: Calculate h1 activation (Specialist 1)</strong><br>
      z1 = 20·<span class="val-A">0</span> + (-20)·<span class="val-B">0</span> + (-10)<br>
      z1 = <span class="calc-z1">-10.0</span><br>
      h1 = ReLU(z1) = max(0, <span class="calc-z1">-10.0</span>) = <span class="calc-h1">0.0</span>
    </div>
    
    <div class="calc-step">
      <strong>Step 2: Calculate h2 activation (Specialist 2)</strong><br>
      z2 = (-20)·<span class="val-A">0</span> + 20·<span class="val-B">0</span> + (-10)<br>
      z2 = <span class="calc-z2">-10.0</span><br>
      h2 = ReLU(z2) = max(0, <span class="calc-z2">-10.0</span>) = <span class="calc-h2">0.0</span>
    </div>
    
    <div class="calc-step">
      <strong>Step 3: Calculate output y (Coordinator)</strong><br>
      z3 = 20·<span class="calc-h1">0.0</span> + 20·<span class="calc-h2">0.0</span> + (-30)<br>
      z3 = <span class="calc-z3">-30.0</span><br>
      y = σ(z3) = 1 / (1 + e<sup>-(-30.0)</sup>) = <span class="calc-y">0.0000</span><br>
      y = <span class="calc-y-pct">0.0%</span><br>
      Since 0.0% < 50%, the verdict is <strong>0</strong>.
    </div>
  </div>
  
  <!-- Truth table -->
  <div class="truth-table-container">
    <h3>📋 Dynamic truth table</h3>
    <table class="truth-table">
      <thead>
        <tr>
          <th>A</th>
          <th>B</th>
          <th>Expected XOR</th>
          <th>MLP predicts</th>
          <th>Result</th>
        </tr>
      </thead>
      <tbody id="truth-table-body">
        <tr>
          <td>0</td>
          <td>0</td>
          <td>0</td>
          <td class="pending">—</td>
          <td class="pending">—</td>
        </tr>
        <tr>
          <td>0</td>
          <td>1</td>
          <td>1</td>
          <td class="pending">—</td>
          <td class="pending">—</td>
        </tr>
        <tr>
          <td>1</td>
          <td>0</td>
          <td>1</td>
          <td class="pending">—</td>
          <td class="pending">—</td>
        </tr>
        <tr>
          <td>1</td>
          <td>1</td>
          <td>0</td>
          <td class="pending">—</td>
          <td class="pending">—</td>
        </tr>
      </tbody>
    </table>
  </div>
  
  <!-- Explanation section -->
  <div class="explanation-section">
    <h3>🧠 Why does it work?</h3>
    <p>The Multilayer Perceptron solves XOR through a <strong>team of specialists</strong>:</p>
    
    <div class="specialist-box">
      <h4>Specialist 1 (h1): Detects (1,0)</h4>
      <p>Activates only when A=1 and B=0. With weights [20, -20] and bias -10:</p>
      <p>• If A=1, B=0: 20·1 + (-20)·0 - 10 = 10 → ReLU(10) = 10 ✅</p>
      <p>• If A=0, B=1: 20·0 + (-20)·1 - 10 = -30 → ReLU(-30) = 0 ❌</p>
    </div>
    
    <div class="specialist-box">
      <h4>Specialist 2 (h2): Detects (0,1)</h4>
      <p>Activates only when A=0 and B=1. With weights [-20, 20] and bias -10:</p>
      <p>• If A=0, B=1: (-20)·0 + 20·1 - 10 = 10 → ReLU(10) = 10 ✅</p>
      <p>• If A=1, B=0: (-20)·1 + 20·0 - 10 = -30 → ReLU(-30) = 0 ❌</p>
    </div>
    
    <div class="specialist-box">
      <h4>Coordinator (y): Sums the specialists</h4>
      <p>With weights [20, 20] and bias -30:</p>
      <p>• If exactly ONE activates (XOR=1): 20·10 + 20·0 - 30 = 170 → σ(170) ≈ 100%</p>
      <p>• If none activate (0,0): 20·0 + 20·0 - 30 = -30 → σ(-30) ≈ 0% ✅</p>
    </div>
  </div>
  
  <!-- Nonlinear XOR diagram -->
  <div class="nonlinear-diagram">
    <h3>📐 Why is XOR not linearly separable?</h3>
    <svg viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg" style="max-width: 25rem; margin: 0 auto; display: block;">
      <!-- Axes -->
      <line x1="50" y1="250" x2="350" y2="250" stroke="#333" stroke-width="2"/>
      <line x1="50" y1="250" x2="50" y2="50" stroke="#333" stroke-width="2"/>
      <text x="360" y="255" font-size="14" fill="#333">A</text>
      <text x="30" y="40" font-size="14" fill="#333">B</text>
      
      <!-- Points -->
      <circle cx="100" cy="200" r="12" fill="#4caf50"/>
      <text x="100" y="225" text-anchor="middle" font-size="12" fill="#333">(0,0)→0</text>
      
      <circle cx="300" cy="100" r="12" fill="#4caf50"/>
      <text x="300" y="85" text-anchor="middle" font-size="12" fill="#333">(1,1)→0</text>
      
      <circle cx="100" cy="100" r="12" fill="#f44336"/>
      <text x="100" y="85" text-anchor="middle" font-size="12" fill="#333">(0,1)→1</text>
      
      <circle cx="300" cy="200" r="12" fill="#f44336"/>
      <text x="300" y="225" text-anchor="middle" font-size="12" fill="#333">(1,0)→1</text>
      
      <!-- Line attempting to separate -->
      <line x1="80" y1="150" x2="320" y2="150" stroke="#ff9800" stroke-width="2" stroke-dasharray="5,5"/>
      <text x="200" y="140" text-anchor="middle" font-size="11" fill="#ff9800">A single line?</text>
      
      <!-- Legend -->
      <circle cx="80" cy="280" r="8" fill="#f44336"/>
      <text x="95" y="285" font-size="11" fill="#333">XOR = 1</text>
      <circle cx="200" cy="280" r="8" fill="#4caf50"/>
      <text x="215" y="285" font-size="11" fill="#333">XOR = 0</text>
    </svg>
    <p style="text-align: center; margin-top: 0.9375rem; color: #e65100;">
      <strong>There is no single straight line that separates the red points (XOR=1) from the green ones (XOR=0).</strong><br>
      That is why a Simple Perceptron (a single neuron) cannot solve XOR.<br>
      The MLP uses two hidden neurons to create <strong>two decision boundaries</strong>.
    </p>
  </div>
  
  <!-- Instructions -->
  <div class="instructions">
    <h3>💾 Usage instructions</h3>
    <ol>
      <li>Save this HTML file to your computer (Ctrl+S or Cmd+S)</li>
      <li>Open it with any web browser (Chrome, Firefox, Safari, Edge)</li>
      <li>No internet connection or software installation required</li>
      <li>Works on Windows, macOS, Linux, Android, iOS</li>
      <li>Experiment with different combinations of A and B</li>
      <li>Observe how the activations and connection weights change</li>
    </ol>
  </div>
  
</div>

<!-- License footer -->
<div class="license-footer">
  <p>
    <a href="https://proyectowww-it.blogspot.com/"><strong>C.L.A.R.A.</strong></a> — Clear Lexical Analysis &amp; Reasoning Aid<br>
    Part of <a href="https://www.proyectowww.com.ar/" target="_blank" rel="noopener">Proyecto WWW</a> (Spanish version)  · Part of <a href="https://proyectowww-en.blogspot.com/" target="_blank" rel="noopener">Proyecto WWW</a> (English version).<br>
    <a href="https://www.proyectowww.com.ar/contrato" target="_blank" rel="noopener">Terms of use (ES)</a> · <a href="https://proyectowww-en.blogspot.com/contrato" target="_blank" rel="noopener">Terms of use (EN)</a>
  </p>
  <p style="margin-top:0.625rem;opacity:0.8;">
    <span>© 2026 <a href="https://www.proyectowww.com.ar/2012/05/autor.html" target="_blank" rel="noopener noreferrer">Jorge Verón Schenone</a></span>
  </p>
</div>

<script>
// Pre-trained weights for XOR
var weights = {
  w11: 20,
  w12: -20,
  w21: -20,
  w22: 20,
  w31: 20,
  w32: 20,
  b1: -10,
  b2: -10,
  b3: -30
};

function relu(x) { return Math.max(0, x); }
function sigmoid(x) { return 1 / (1 + Math.exp(-x)); }

function updateNetwork() {
  var A = document.getElementById('inputA').checked ? 1 : 0;
  var B = document.getElementById('inputB').checked ? 1 : 0;
  
  document.getElementById('value-A').textContent = A;
  document.getElementById('value-B').textContent = B;
  
  var z1 = weights.w11 * A + weights.w12 * B + weights.b1;
  var h1 = relu(z1);
  var z2 = weights.w21 * A + weights.w22 * B + weights.b2;
  var h2 = relu(z2);
  
  document.getElementById('value-h1').textContent = h1.toFixed(1);
  document.getElementById('value-h2').textContent = h2.toFixed(1);
  
  var h1Color = h1 > 0 ? 'rgba(76, 175, 80, ' + Math.min(h1/10, 1) + ')' : '#fff3e0';
  var h2Color = h2 > 0 ? 'rgba(76, 175, 80, ' + Math.min(h2/10, 1) + ')' : '#fff3e0';
  document.querySelector('#neuron-h1 circle').setAttribute('fill', h1Color);
  document.querySelector('#neuron-h2 circle').setAttribute('fill', h2Color);
  
  var z3 = weights.w31 * h1 + weights.w32 * h2 + weights.b3;
  var y = sigmoid(z3);
  
  document.getElementById('value-y').textContent = (y * 100).toFixed(1) + '%';
  var yColor = y > 0.5 ? 'rgba(76, 175, 80, ' + y + ')' : 'rgba(244, 67, 54, ' + (1-y) + ')';
  document.querySelector('#neuron-y circle').setAttribute('fill', yColor);
  
  updateConnectionColor('conn-A-h1', weights.w11, A * h1);
  updateConnectionColor('conn-A-h2', weights.w21, A * h2);
  updateConnectionColor('conn-B-h1', weights.w12, B * h1);
  updateConnectionColor('conn-B-h2', weights.w22, B * h2);
  updateConnectionColor('conn-h1-y', weights.w31, h1 * y);
  updateConnectionColor('conn-h2-y', weights.w32, h2 * y);
  
  document.getElementById('calc-h1').textContent = z1.toFixed(1);
  document.getElementById('result-h1').textContent = h1.toFixed(1);
  document.getElementById('calc-h2').textContent = z2.toFixed(1);
  document.getElementById('result-h2').textContent = h2.toFixed(1);
  document.getElementById('calc-y').textContent = z3.toFixed(1);
  document.getElementById('result-y').textContent = (y * 100).toFixed(1) + '%';
  
  updateDetailedCalculation(A, B, z1, h1, z2, h2, z3, y);
  
  var predicted = y > 0.5 ? 1 : 0;
  var expected = A ^ B;
  var correct = predicted === expected;
  
  var resultBox = document.getElementById('result-box');
  resultBox.textContent = 'XOR(' + A + ', ' + B + ') = ' + predicted + ' ' + (correct ? '\u2713' : '\u2717');
  resultBox.className = 'result-box ' + (correct ? 'correct' : 'incorrect');
  
  updateTruthTable(A, B, predicted, expected, correct);
}

function updateConnectionColor(connId, weight, activation) {
  var conn = document.getElementById(connId);
  var intensity = Math.min(Math.abs(activation) / 10, 1);
  if (weight > 0) {
    conn.setAttribute('stroke', 'rgba(76, 175, 80, ' + intensity + ')');
  } else {
    conn.setAttribute('stroke', 'rgba(244, 67, 54, ' + intensity + ')');
  }
  conn.setAttribute('stroke-width', 2 + intensity * 3);
}

function updateDetailedCalculation(A, B, z1, h1, z2, h2, z3, y) {
  var elsA = document.querySelectorAll('.val-A');
  for (var i = 0; i < elsA.length; i++) elsA[i].textContent = A;
  var elsB = document.querySelectorAll('.val-B');
  for (var i = 0; i < elsB.length; i++) elsB[i].textContent = B;
  var elsZ1 = document.querySelectorAll('.calc-z1');
  for (var i = 0; i < elsZ1.length; i++) elsZ1[i].textContent = z1.toFixed(1);
  var elsH1 = document.querySelectorAll('.calc-h1');
  for (var i = 0; i < elsH1.length; i++) elsH1[i].textContent = h1.toFixed(1);
  var elsZ2 = document.querySelectorAll('.calc-z2');
  for (var i = 0; i < elsZ2.length; i++) elsZ2[i].textContent = z2.toFixed(1);
  var elsH2 = document.querySelectorAll('.calc-h2');
  for (var i = 0; i < elsH2.length; i++) elsH2[i].textContent = h2.toFixed(1);
  var elsZ3 = document.querySelectorAll('.calc-z3');
  for (var i = 0; i < elsZ3.length; i++) elsZ3[i].textContent = z3.toFixed(1);
  var elsY = document.querySelectorAll('.calc-y');
  for (var i = 0; i < elsY.length; i++) elsY[i].textContent = y.toFixed(4);
  var elsYPct = document.querySelectorAll('.calc-y-pct');
  for (var i = 0; i < elsYPct.length; i++) elsYPct[i].textContent = (y * 100).toFixed(1) + '%';
}

var testedCases = {};

function updateTruthTable(A, B, predicted, expected, correct) {
  var rows = document.querySelectorAll('.truth-table tbody tr');
  var rowIndex = A * 2 + B;
  if (rowIndex >= rows.length) return;
  var row = rows[rowIndex];
  row.classList.add('tested');
  row.cells[3].textContent = predicted;
  row.cells[3].classList.remove('pending');
  row.cells[4].innerHTML = correct ? '<span class="check-mark">✓</span>' : '<span class="cross-mark">✗</span>';
  row.cells[4].classList.remove('pending');
}

function setXOR(A, B) {
  document.getElementById('inputA').checked = A === 1;
  document.getElementById('inputB').checked = B === 1;
  var btns = document.querySelectorAll('.xor-btn');
  for (var i = 0; i < btns.length; i++) btns[i].classList.remove('active');
  var btnIndex = A * 2 + B;
  document.querySelectorAll('.xor-btn')[btnIndex].classList.add('active');
  updateNetwork();
}

function toggleCalculation() {
  var details = document.getElementById('calculation-details');
  details.classList.toggle('visible');
  var button = document.querySelector('.calculation-toggle');
  button.textContent = details.classList.contains('visible') ? '📝 Hide full calculation [▲]' : '📝 Show full calculation [▼]';
}

updateNetwork();
</script>

</body>
</html>

Works in any browser. No installation or internet connection required.