6  Interactive Colab Guide

Open In Colab

6.1 HTML + JavaScript

Hyper-Text Markup Language, HTML, is the most common programming paradigm in the world, by sheer volume. Every webpage is built on it.

Colab, even if originally aimed at python, has a feature to build magic cells. Preceed html cells with %%html to tell colab to interpret it as such.

Inside it, you can use Javascript as any webpage would.

%%html
<input id="user-input" type="number" value="2">
<button onclick="calculate()">Compute Square</button>

<p id="calc-result"></p>

<script>
function calculate() {
    // 1. Get the number the student typed
    let value = document
        .getElementById("user-input")
        .value;

    // 2. Do the math
    let result = value * value;

    // 3. Show the result
    document
        .getElementById("calc-result")
        .innerText = "Result: " + result;
    }
    </script>

6.2 SVG Integration

You can also embed Scalable Vector Graphics (SVG) directly into your Colab notebook using the same %%html magic command.

%%html
<svg width="200" height="200">
    <circle cx="100"
            cy="100"
            r="80"
            stroke="red" stroke-width="4"
            fill="tomato" />
    <text
        x="50%" y="50%"
        text-anchor="middle"
        fill="black" font-size="20px"
        dy=".3em">SVG Circle</text>
    </svg>
SVG Circle

7 Animating svg

%%html
<svg width="400" height="400">
    <circle id="animated-circle"
            cx="50%"
            cy="50%"
            r="80"
            stroke="red" stroke-width="4"
            fill="tomato" />
    <text
        x="50%" y="50%"
        text-anchor="middle"
        fill="black" font-size="20px"
        dy=".3em">SVG Circle</text>
</svg>
<br>
<button onclick="startAnimation()">Animate Circle</button>

<script>
let animating = false;
let r = 80;
let growing = false;

function startAnimation() {
    if (animating) return; // Prevent multiple loops
    animating = true;
    let circle = document.getElementById("animated-circle");

    function frame() {
        if (growing) {
            r += 0.1;
            if (r >= 120) growing = false;
        } else {
            r -= 1;
            if (r <= 80) growing = true;
        }
        circle.setAttribute("r", r);
        requestAnimationFrame(frame);
    }

    requestAnimationFrame(frame);
}
</script>
SVG Circle
%%html
<input type="range" min="0" max="10" value="5" id="my-slider" oninput="updateSlider()">

<p id="slider-display"></p>

<script>
function updateSlider() {
  let v = document.getElementById("my-slider").value;
  document.getElementById("slider-display").innerText = "Current Value: " + v;
}
</script>

%%html
<div style="text-align: center; width: 300px;">
  <svg width="200" height="200">
    <!-- A rectangle with rx and ry equal to half its width/height is a circle -->
    <rect id="morph-shape"
          x="10" y="10"
          width="180" height="180"
          rx="90" ry="90"
          fill="tomato"
          stroke="red" stroke-width="4" />
  </svg>
  <br><br>
  <input type="range" id="morph-slider" min="0" max="90" value="90" oninput="morphShape()" style="width: 100%;">
  <p id="morph-label">Shape: Circle (rx=90)</p>
</div>

<script>
function morphShape() {
  let sliderValue = document.getElementById("morph-slider").value;
  let shape = document.getElementById("morph-shape");
  let label = document.getElementById("morph-label");

  // Update the corner radius
  shape.setAttribute("rx", sliderValue);
  shape.setAttribute("ry", sliderValue);

  // Update the label text
  if (sliderValue == 0) {
      label.innerText = "Shape: Square (rx=0)";
  } else if (sliderValue == 90) {
      label.innerText = "Shape: Circle (rx=90)";
  } else {
      label.innerText = "Shape: Squircle (rx=" + sliderValue + ")";
  }
}
</script>


Shape: Circle (rx=90)

%%html
<canvas id="myChart"></canvas>

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>

<script>
let ctx = document.getElementById('myChart');

let chart = new Chart(ctx, {
  type: 'line', // You can change this to 'bar' or 'pie'
  data: {
    labels: [0, 1, 2, 3, 4], // The X-axis
    datasets: [{
      label: 'y = x^2',
      data: [0, 1, 4, 9, 16]  // The Y-axis
    }]
  }
});
</script>
%%html
<input id="answer" type="number">
<button onclick="check()">Submit</button>
<p id="feedback"></p>
<script>
  function check() {
    // Your grading logic here
  }
</script

8 Shinny Live

!pip install shinylive
Requirement already satisfied: shinylive in /usr/local/lib/python3.12/dist-packages (0.8.6)
Requirement already satisfied: shiny in /usr/local/lib/python3.12/dist-packages (from shinylive) (1.6.0)
Requirement already satisfied: click>=8.1.7 in /usr/local/lib/python3.12/dist-packages (from shinylive) (8.3.1)
Requirement already satisfied: appdirs>=1.4.4 in /usr/local/lib/python3.12/dist-packages (from shinylive) (1.4.4)
Requirement already satisfied: lzstring>=1.0.4 in /usr/local/lib/python3.12/dist-packages (from shinylive) (1.0.4)
Requirement already satisfied: typing-extensions>=4.0.1 in /usr/local/lib/python3.12/dist-packages (from shinylive) (4.15.0)
Requirement already satisfied: setuptools in /usr/local/lib/python3.12/dist-packages (from shinylive) (75.2.0)
Requirement already satisfied: chevron>=0.14.0 in /usr/local/lib/python3.12/dist-packages (from shinylive) (0.14.0)
Requirement already satisfied: future>=0.14.0 in /usr/local/lib/python3.12/dist-packages (from lzstring>=1.0.4->shinylive) (1.0.0)
Requirement already satisfied: uvicorn>=0.16.0 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (0.42.0)
Requirement already satisfied: starlette in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (0.52.1)
Requirement already satisfied: websockets>=13.0 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (15.0.1)
Requirement already satisfied: htmltools>=0.6.0 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (0.6.0)
Requirement already satisfied: markdown-it-py>=1.1.0 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (4.0.0)
Requirement already satisfied: mdit-py-plugins>=0.3.0 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (0.5.0)
Requirement already satisfied: linkify-it-py>=1.0 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (2.1.0)
Requirement already satisfied: platformdirs>=2.1.0 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (4.9.4)
Requirement already satisfied: asgiref>=3.5.2 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (3.11.1)
Requirement already satisfied: packaging>=20.9 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (26.0)
Requirement already satisfied: watchfiles>=0.18.0 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (1.1.1)
Requirement already satisfied: questionary>=2.0.0 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (2.1.1)
Requirement already satisfied: prompt-toolkit in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (3.0.52)
Requirement already satisfied: python-multipart>=0.0.7 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (0.0.22)
Requirement already satisfied: narwhals>=1.10.0 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (2.18.0)
Requirement already satisfied: orjson>=3.10.7 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (3.11.7)
Requirement already satisfied: shinychat>=0.1.0 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (0.2.9)
Requirement already satisfied: opentelemetry-api>=1.20.0 in /usr/local/lib/python3.12/dist-packages (from shiny->shinylive) (1.38.0)
Requirement already satisfied: uc-micro-py in /usr/local/lib/python3.12/dist-packages (from linkify-it-py>=1.0->shiny->shinylive) (2.0.0)
Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.12/dist-packages (from markdown-it-py>=1.1.0->shiny->shinylive) (0.1.2)
Requirement already satisfied: importlib-metadata<8.8.0,>=6.0 in /usr/local/lib/python3.12/dist-packages (from opentelemetry-api>=1.20.0->shiny->shinylive) (8.7.1)
Requirement already satisfied: wcwidth in /usr/local/lib/python3.12/dist-packages (from prompt-toolkit->shiny->shinylive) (0.6.0)
Requirement already satisfied: h11>=0.8 in /usr/local/lib/python3.12/dist-packages (from uvicorn>=0.16.0->shiny->shinylive) (0.16.0)
Requirement already satisfied: anyio>=3.0.0 in /usr/local/lib/python3.12/dist-packages (from watchfiles>=0.18.0->shiny->shinylive) (4.12.1)
Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.12/dist-packages (from anyio>=3.0.0->watchfiles>=0.18.0->shiny->shinylive) (3.11)
Requirement already satisfied: zipp>=3.20 in /usr/local/lib/python3.12/dist-packages (from importlib-metadata<8.8.0,>=6.0->opentelemetry-api>=1.20.0->shiny->shinylive) (3.23.0)

8.1 Integrating with Quarto (GitHub Pages)

If you are building a Quarto book or website and hosting it on a static server like GitHub Pages, you cannot run a live Python server in the background. Instead, you use Shinylive, which runs Python entirely inside the user’s browser using WebAssembly.

To enable this, open your terminal, navigate to your Quarto project folder, and run the following command to install the extension:

from shiny import App, render, ui
import numpy as np
import matplotlib.pyplot as plt

# 1. The User Interface
app_ui = ui.page_fluid(
    ui.h3("Explore a Sine Wave"),
    ui.input_slider("frequency", "Wave Frequency", min=1, max=10, value=5),
    ui.output_plot("wave_plot")
)

# 2. The Logic
def server(input, output, session):
    @output
    @render.plot
    def wave_plot():
        # Get the slider value
        freq = input.frequency()
        # Calculate the math
        x = np.linspace(0, 2 * np.pi, 100)
        y = np.sin(freq * x)
        # Draw the plot
        fig, ax = plt.subplots()
        ax.plot(x, y)
        ax.set_ylim(-1.5, 1.5)
        return fig

# 3. Create App
app = App(app_ui, server)

# 4. Simply call the app object to render it inline in Colab!
app
<shiny._app.App at 0x7e06dda54080>