April 12, 2026 ·
I posted this widget yesterday showcasing the Central Limit Theorem visualizing how quickly a sample n of even 2 or 3 measurements converge to a bell or normal curve, shout-out Johann Carl Friedrich Gauss. Two assumptions underpinning central-limit style Gaussian distributions are 1) that samples are independent from each other, and 2) that samples have finite variability. It's important to mention these two assumptions because they're implicit every time one sees a Gaussian function but almost never explicitly stated.
I thought I'd try another similar exhibition today and visualize what's called the Law of Large Numbers, a similar basic assumption underpinning almost any and every statistically oriented analysis. This law of large numbers states that the average of the results obtained from a large number of independent random samples converges to the true value, if it exists. In other words, coin tosses conducted fairly over time will converge to a 50% chance at head or tails. What's important to understand here is that again, each coin toss instance is independent and variations in their outcomes are limited to either heads or tails. If these assumptions are true, convergence to the average happens over a large number of samples.
Here is the Python code I used, generated with the help of Google Gemini:
import gradio as gr
import matplotlib.pyplot as plt
import random
def simulate_lln(n):
flips = [random.choice([0, 1]) for _ in range(n)]
averages = [sum(flips[:i+1]) / (i+1) for i in range(n)]
# Smaller, tighter figure size for clean embedding
fig, ax = plt.subplots(figsize=(6, 3))
# Minimalist styling
ax.plot(range(1, n+1), averages, marker='o', color='black', markersize=4)
ax.axhline(y=0.5, color='gray', linestyle='--')
# Strip away the top and right box borders
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.set_ylim(-0.1, 1.1)
ax.set_title(f"Average after {n} flips: {averages[-1]:.2f}")
# I REMOVED the set_alpha(0.0) lines here so the graph stays bright and clear!
plt.tight_layout()
return fig
# Custom CSS to hide the Gradio footer watermark and borders
custom_css = """
footer {display: none !important;}
.gradio-container {border: none !important;}
"""
# Build the layout using the cleaner gr.Blocks method
with gr.Blocks(css=custom_css) as demo:
# 1. Define the UI elements
n_slider = gr.Slider(minimum=1, maximum=100, step=1, value=10, label="Number of Flips")
plot_output = gr.Plot(show_label=False)
# 2. Tell the slider to update the plot whenever it moves
n_slider.change(fn=simulate_lln, inputs=n_slider, outputs=plot_output)
# 3. Tell the app to draw the plot immediately when the page loads
demo.load(fn=simulate_lln, inputs=n_slider, outputs=plot_output)
demo.launch()
comments section