Created
February 12, 2025 11:35
-
-
Save dutta-alankar/d3e8086545c32631bfa344c65aa8ae3e to your computer and use it in GitHub Desktop.
A simple html script solving and ODE with python using Pyodide
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>ODE Solver (Enhanced)</title> | |
<script src="https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.js"></script> | |
<style> | |
:root { | |
--bg-color: white; | |
--text-color: black; | |
--btn-bg: #ddd; | |
--btn-text: black; | |
} | |
[data-theme="dark"] { | |
--bg-color: #1e1e1e; | |
--text-color: white; | |
--btn-bg: #444; | |
--btn-text: white; | |
} | |
body { | |
background-color: var(--bg-color); | |
color: var(--text-color); | |
font-family: Arial, sans-serif; | |
text-align: center; | |
transition: background 0.3s, color 0.3s; | |
} | |
button { | |
background-color: var(--btn-bg); | |
color: var(--btn-text); | |
border: none; | |
padding: 10px; | |
margin: 10px; | |
cursor: pointer; | |
} | |
img { | |
border: 1px solid var(--text-color); | |
opacity: 0; | |
transition: opacity 0.5s ease-in-out; | |
} | |
.loading { | |
font-size: 1.5em; | |
font-weight: bold; | |
animation: blink 1s infinite; | |
} | |
@keyframes blink { | |
0% { opacity: 1; } | |
50% { opacity: 0.5; } | |
100% { opacity: 1; } | |
} | |
</style> | |
</head> | |
<body> | |
<h1>ODE Solver (Python in Browser)</h1> | |
<button onclick="toggleTheme()">Toggle Light/Dark Mode</button> | |
<br><br> | |
<label for="k-slider">Decay Rate (k): <span id="k-value">1.0</span></label> | |
<input type="range" id="k-slider" min="0.1" max="2.0" step="0.1" value="1.0" oninput="solveODE()"> | |
<br><br> | |
<label>Initial Condition y(0):</label> | |
<select id="y0-select" onchange="solveODE()"> | |
<option value="1">1</option> | |
<option value="2">2</option> | |
<option value="5">5</option> | |
</select> | |
<br><br> | |
<p id="loading" class="loading" style="display:none;">Solving ODE...</p> | |
<img id="plot" src="" alt="ODE Solution will appear here"> | |
<script> | |
function detectSystemTheme() { | |
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; | |
} | |
function applyTheme(theme) { | |
document.documentElement.setAttribute("data-theme", theme); | |
localStorage.setItem("theme", theme); | |
} | |
function toggleTheme() { | |
let currentTheme = document.documentElement.getAttribute("data-theme"); | |
let newTheme = currentTheme === "dark" ? "light" : "dark"; | |
applyTheme(newTheme); | |
solveODE(); | |
} | |
function loadTheme() { | |
let savedTheme = localStorage.getItem("theme") || detectSystemTheme(); | |
applyTheme(savedTheme); | |
} | |
async function loadPyodideAndPackages() { | |
window.pyodide = await loadPyodide(); | |
await pyodide.loadPackage(["numpy", "scipy", "matplotlib"]); | |
solveODE(); // Solve once when page loads | |
} | |
async function solveODE() { | |
let k = parseFloat(document.getElementById("k-slider").value); | |
let y0 = parseFloat(document.getElementById("y0-select").value); | |
document.getElementById("k-value").innerText = k; | |
let theme = document.documentElement.getAttribute("data-theme"); | |
// Show loading animation | |
document.getElementById("loading").style.display = "block"; | |
document.getElementById("plot").style.opacity = "0"; | |
let pythonCode = ` | |
import numpy as np | |
import matplotlib | |
import matplotlib.pyplot as plt | |
from scipy.integrate import solve_ivp | |
plt.style.use("dark_background" if "${theme}" == "dark" else "default") | |
## Plot Styling | |
matplotlib.rcParams["xtick.direction"] = "in" | |
matplotlib.rcParams["ytick.direction"] = "in" | |
matplotlib.rcParams["xtick.top"] = False | |
matplotlib.rcParams["ytick.right"] = True | |
matplotlib.rcParams["xtick.minor.visible"] = True | |
matplotlib.rcParams["ytick.minor.visible"] = True | |
matplotlib.rcParams["axes.grid"] = True | |
matplotlib.rcParams["grid.linestyle"] = ":" | |
matplotlib.rcParams["grid.linewidth"] = 0.8 | |
matplotlib.rcParams["grid.color"] = "gray" | |
matplotlib.rcParams["grid.alpha"] = 0.3 | |
matplotlib.rcParams["lines.dash_capstyle"] = "round" | |
matplotlib.rcParams["lines.solid_capstyle"] = "round" | |
matplotlib.rcParams["legend.handletextpad"] = 0.4 | |
matplotlib.rcParams["axes.linewidth"] = 1.0 | |
matplotlib.rcParams["lines.linewidth"] = 3.5 | |
matplotlib.rcParams["ytick.major.width"] = 1.2 | |
matplotlib.rcParams["xtick.major.width"] = 1.2 | |
matplotlib.rcParams["ytick.minor.width"] = 1.0 | |
matplotlib.rcParams["xtick.minor.width"] = 1.0 | |
matplotlib.rcParams["ytick.major.size"] = 11.0 | |
matplotlib.rcParams["xtick.major.size"] = 11.0 | |
matplotlib.rcParams["ytick.minor.size"] = 5.0 | |
matplotlib.rcParams["xtick.minor.size"] = 5.0 | |
matplotlib.rcParams["xtick.major.pad"] = 10.0 | |
matplotlib.rcParams["xtick.minor.pad"] = 10.0 | |
matplotlib.rcParams["ytick.major.pad"] = 6.0 | |
matplotlib.rcParams["ytick.minor.pad"] = 6.0 | |
matplotlib.rcParams["xtick.labelsize"] = 26.0 | |
matplotlib.rcParams["ytick.labelsize"] = 26.0 | |
matplotlib.rcParams["axes.titlesize"] = 24.0 | |
matplotlib.rcParams["axes.labelsize"] = 28.0 | |
matplotlib.rcParams["axes.labelpad"] = 8.0 | |
plt.rcParams["font.size"] = 28 | |
matplotlib.rcParams["legend.handlelength"] = 2 | |
# matplotlib.rcParams["figure.dpi"] = 200 | |
matplotlib.rcParams["axes.axisbelow"] = True | |
matplotlib.rcParams["figure.figsize"] = (13,10) | |
def ode(t, y): | |
return -${k} * y | |
t_span = (0, 10) | |
t_eval = np.linspace(*t_span, 100) | |
sol = solve_ivp(ode, t_span, [${y0}], t_eval=t_eval, method="BDF") | |
plt.figure() | |
plt.plot(sol.t, sol.y[0], label="y(t)") | |
plt.xlabel("Time") | |
plt.ylabel("y") | |
plt.title("ODE Solution using BDF") | |
plt.legend() | |
import io | |
import base64 | |
buf = io.BytesIO() | |
plt.savefig(buf, format="png", transparent=True) | |
buf.seek(0) | |
"data:image/png;base64," + base64.b64encode(buf.read()).decode() | |
`; | |
let result = await pyodide.runPythonAsync(pythonCode); | |
// Hide loading and fade in plot | |
document.getElementById("loading").style.display = "none"; | |
document.getElementById("plot").src = result; | |
document.getElementById("plot").style.opacity = "1"; | |
} | |
loadTheme(); | |
loadPyodideAndPackages(); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment