Skip to content

s243a/SciREPL

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

254 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SciREPL — Mobile Multi-Language Scientific REPL

A mobile-first scientific REPL powered by WebAssembly runtimes + Capacitor, with Jupyter-style notebook features. Supports Python (Pyodide), R (webR), Prolog (swipl-wasm), Bash (brush-wasm), and JavaScript (native).

Status License

Features

  • Multi-language notebooks — Python, R, Prolog, Bash, and JavaScript in the same notebook, with per-cell language tracking
  • Offline Python via Pyodide (WASM) — NumPy + SymPy preloaded, %pip install for PyPI packages
  • SWI-Prolog kernel — Full SWI-Prolog via swipl-wasm, loaded on demand from CDN
  • Bash kernel — Unix shell via brush-wasm with coreutils, findutils, grep (all Rust reimplementations)
  • JavaScript kernel — Native browser JS execution with zero download. Direct access to WASM modules, SharedVFS, and browser APIs
  • R kernel — Full R via webR (WASM), loaded on demand (~50 MB, cached after first use). Supports plotting, install.packages(), and SharedVFS file sharing.
  • TypR kernel — Typed R superset via typr-wasm (~1.7 MB). Compiles TypR to R, then executes through webR. Type checking, #!transpile and #!show-r directives.
  • Kernel abstraction layer — Pluggable architecture for adding new language runtimes
  • Package system v2 — Install packages with notebooks, data files, Python modules, Prolog knowledge bases, and WASM libraries. See docs/packages.md.
  • SharedVFS — In-memory filesystem shared across all kernels. Python, Bash, Prolog, R, and JavaScript can read/write the same files. Persisted to IndexedDB — files survive page reloads.
  • Cross-kernel WASM FFI — Package and distribute pre-compiled Rust WASM libraries callable from JavaScript, Python, and Prolog
  • Rich output — LaTeX math rendering, interactive Plotly charts, tables
  • Hybrid plotting — Python plot() → Plotly.js (pinch-zoom, pan, hover), R plotly() → interactive Plotly charts
  • Matplotlib supportimport matplotlib.pyplot as plt; plt.show() renders inline PNG images (dark theme)
  • Syntax highlighting — Code cells display with keyword coloring via highlight.js (Python, JavaScript, R, Bash, Prolog)
  • Find & ReplaceCtrl+F / Cmd+F or header search button (mobile-friendly) to search across all cells with match navigation and replace
  • Editable cells — Click the pencil icon to edit and re-run any cell
  • Delete cells — Remove individual cells with one click
  • Cell reordering — Drag-and-drop (desktop) or move up/down arrows (mobile)
  • Markdown cells — Toggle Code/Md, supports $LaTeX$ and $$display math$$
  • Run All Below / Run All Cells — Re-execute from a cell downward or the entire notebook
  • File browser — Browse, download, create folders, and manage files across all filesystems. Upload files to any folder, extract zip archives, or upload them as whole files.
  • Multi-notebook tabs — Multiple notebooks in tabs/sidebar/dropdown. Import a workbook to create a new tab. Double-click tab names to rename.
  • Session persistence — Cells auto-save (with language) and restore on app restart. SharedVFS files persist to IndexedDB.
  • Import/Export.ipynb, .py, .pl with language-aware metadata; native share sheet
  • Rich export — HTML, Markdown, PDF, DOCX, and LaTeX via Export modal with theme (dark/light/browser default), page background, and image handling options. Exports include code, output, plots, LaTeX math, and tables. HTML and DOCX exports include syntax-highlighted code blocks.
  • Import/Export with outputs.ipynb export includes cell outputs (text, images, LaTeX, tables), viewable in Jupyter and GitHub without re-execution. Import preserves outputs — no re-execution needed.
  • Package catalog — Browse and one-click install curated packages
  • Math Mode palette — Quick-insert SymPy functions (diff, integrate, solve, etc.)
  • Variable persistence across cells (like Jupyter)
  • Semicolon suppression (MATLAB/IPython-style)
  • Command history — Arrow keys to recall previous inputs
  • Mobile-first UI — Dark theme, touch-friendly
  • Installable PWA — Install from browser as a desktop or mobile app, works offline after first load
  • Privacy-first — Bundled rendering libraries, lazy CDN loading with consent prompt on first use
  • Lazy kernel loading — App starts instantly; Python, R, and Prolog runtimes download only when first used
  • Settings menu — Configure auto-execute on import, delete confirmation, export format (.zip/.tar/.tar.gz), auto-download runtimes, auto-switch workbook on install, large touch targets, default language

Future Features

Some of these may be offered as part of a Pro version.

Quick Start

Run Locally

npm run serve

Open http://localhost:8085. The app loads instantly — runtimes download on first use.

Build for Android

npm install
npx cap sync
cd android && ./gradlew assembleDebug

APK output: android/app/build/outputs/apk/debug/app-debug.apk

Install via ADB

adb install android/app/build/outputs/apk/debug/app-debug.apk

Install as PWA

Visit https://s243a.github.io/SciREPL/ in your browser, then:

  • Chrome (desktop): Click the install icon in the address bar, or Menu > "Install SciREPL"
  • Chrome (Android): Menu > "Add to Home screen" or "Install app"
  • Edge: Click the install icon in the address bar, or Menu > Apps > "Install this site as an app"
  • Safari (iOS): Share button > "Add to Home Screen"

Once installed, it runs in its own window and works offline.

Try It

Python

# Basic math
2 + 2

# NumPy arrays
import numpy as np
np.linspace(0, 10, 5)

# Plotting
x = np.linspace(0, 2*np.pi, 50)
plot(x, np.sin(x))

# SymPy (LaTeX rendering)
from sympy import symbols, diff, sin
x = symbols('x')
diff(sin(x), x)  # Shows cos(x) as rendered LaTeX

# Suppress output
a = np.arange(1000);

Prolog

Switch to Prolog using the language selector (Py → PL):

% Assert facts
assert(parent(tom, bob)).
assert(parent(bob, ann)).

% Query
parent(tom, X).
% → X = bob

% Rules
assert((grandparent(X,Z) :- parent(X,Y), parent(Y,Z))).
grandparent(tom, Z).
% → Z = ann

% Built-in predicates
member(X, [a, b, c]).
% → X = a, X = b, X = c

append([1,2], [3,4], X).
% → X = [1, 2, 3, 4]

R

Switch to R using the language selector (Py → R). First use downloads webR (~50 MB, cached after):

# Basic math
x <- seq(0, 2*pi, length.out=50)
sin(x)

# Data frames
df <- data.frame(name=c("Alice","Bob"), score=c(95, 87))
df

# Interactive plotting
plotly(x, sin(x), title="Sine Wave")

# Install packages
install.packages("jsonlite")
library(jsonlite)

Bash

Switch to Bash using the language selector (Py → Bash):

# Unix commands via brush-wasm
echo "Hello from Bash!"
seq 1 10 | head -5

# File operations (SharedVFS)
echo "data" > /shared/test.txt
cat /shared/test.txt

# Pipes and filters
echo -e "banana\napple\ncherry" | sort

JavaScript

Switch to JavaScript using the language selector (Py → JS):

// Native browser JS — zero download
const x = Array.from({length: 50}, (_, i) => i * 0.1);
const y = x.map(v => Math.sin(v));

// Access SharedVFS
window.sharedVFS.write('/shared/hello.txt', 'from JS');

// Use any browser API
JSON.stringify({pi: Math.PI, e: Math.E}, null, 2)

How Does SciREPL Compare to Jupyter / Colab?

SciREPL Jupyter Notebook Google Colab
Setup Zero — visit a URL or install APK Install Python + pip Google account
Languages per notebook Python, R, Prolog, Bash, JS (per cell) One kernel per notebook Python only
Runs offline Yes (PWA + WASM) Needs local server No
Privacy All execution local Local Google servers
Mobile support Mobile-first + Android app Not optimized Usable but not native
Package ecosystem %pip install (pure-Python PyPI) Full pip Full pip
Performance WASM (slower for heavy compute) Native Python Native + free GPU/TPU
Collaboration Single user JupyterHub Real-time multi-user
Code completion Not yet Extensions available Built-in
WASM FFI Call Rust WASM from any kernel No No
Size ~2MB app + CDN runtimes ~500MB with Anaconda Cloud-based

Why SciREPL? It's 250x smaller than a typical Jupyter install (~2MB vs ~500MB), the only notebook with multi-language cells (Python, Prolog, Bash, JS in one notebook), and built for mobile — not just adapted for it. All with zero setup: visit a URL and go.

Trade-offs: WASM Python is slower than native for heavy compute. %pip install works for pure-Python packages; C-extension packages need pre-compiled WASM wheels. Not ideal for GPU-accelerated ML training or large datasets.

Architecture

graph LR
    A[Input Bar] -->|Code/Markdown| B{Cell Type?}
    B -->|Code| C{Language?}
    B -->|Markdown| D[marked.js + KaTeX]
    C -->|Python| E[Pyodide WASM]
    C -->|Prolog| F[swipl-wasm]
    E -->|text/value| G[Text Output]
    E -->|SymPy object| H[LaTeX via KaTeX]
    E -->|plot call| I[JS Bridge → Plotly.js]
    F -->|solutions| G
Loading

Kernel Architecture

KernelManager (kernel_manager.js)
├── PythonKernel     (kernels/python.js)      — Pyodide + prelude.py + sharedfs bridge
├── PrologKernel     (kernels/prolog.js)       — swipl-wasm + wasm_call/3
├── BashKernel       (kernels/bash.js)         — brush-wasm (coreutils + findutils + grep)
├── JavaScriptKernel (kernels/javascript.js)   — native browser JS (zero download)
├── RKernel          (kernels/r.js)            — webR (lazy-loaded ~50MB, plotting, SharedVFS, install.packages)
└── TypRKernel       (kernels/typr.js)         — typr-wasm (1.7MB) → R transpilation → webR execution

Each kernel implements: init(), execute(code), isReady(), getName(), getLanguage(), destroy()

Kernels are lazy-loaded — only downloaded when first used. Privacy consent and download confirmation are shown before any CDN download. JavaScript and Bash are instant (no CDN required).

SharedVFS + Package System

Package (.zip)  →  PackageLoader  →  target routing
                                      ├── "shared"  →  SharedVFS (/shared/*)
                                      ├── "prolog"  →  Prolog VFS (/user/*)
                                      └── "all"     →  both

SharedVFS (/shared/, /tmp/):
  Bash:    direct access (wasm-bindgen)
  Python:  via sharedfs module (import sharedfs)
  Prolog:  mirrored on read/write
  R:       synced before/after execution (sharedfs_read/write helpers)
  JS:      window.sharedVFS direct access

WASM modules → window.wasmModules[name]
  JS:      window.wasmModules.name.call('func', {args})
  Python:  wasm_call('name', 'func', args)
  Prolog:  wasm_call(name, func, '{"key": "val"}').

See docs/packages.md for full documentation.

File Structure

Capacitor Plugins

  • @capacitor/filesystem — Write export files to device storage
  • @capacitor/share — Native share sheet for file export

CDN Dependencies (loaded at runtime)

Runtime CDN Size When loaded
Pyodide cdn.jsdelivr.net ~25MB First Python cell execution
swipl-wasm SWI-Prolog.github.io ~10MB First Prolog cell execution
webR webr.r-wasm.org ~50MB First R cell execution

Roadmap

  • Multi-language support (Python + Prolog + Bash)
  • Kernel abstraction layer
  • Privacy-first CDN loading (consent before download)
  • Bundled rendering libraries
  • Package system v2 — target routing, binary support, SharedVFS
  • Python SharedVFS bridge (import sharedfs)
  • R SharedVFS bridge (sharedfs_read, sharedfs_write) + install.packages() support
  • Cross-kernel WASM FFI (Python + Prolog can call WASM modules)
  • Package catalog with one-click install
  • JavaScript kernel (native browser, zero download)
  • PWA — installable as desktop/mobile app, offline support, WASM runtime caching
  • R kernel via webR (lazy-loaded, plotting, SharedVFS, package install)
  • Matplotlib inline backend (plt.show() renders PNG images)
  • Interactive R plots via plotly() / mplotly() helpers
  • Rich export — HTML, Markdown, PDF, DOCX, LaTeX with Export modal (theme, page background, image handling)
  • .ipynb export with outputs (text, images, LaTeX, tables)
  • .ipynb import with output preservation (no re-execution needed)
  • Syntax highlighting in exports (HTML, DOCX) via highlight.js
  • In-app syntax highlighting for code cells
  • Find & Replace across notebook cells (Ctrl+F)
  • IndexedDB persistence for SharedVFS (files survive page reloads)
  • Unified file browser with mount-point view (/shared + /mnt/pyodide + /mnt/prolog)
  • File browser: folder selection, download files/folders, create folders, zip extraction
  • Multi-notebook tabs with rename support
  • Workbook import creates new tab (auto-named from heading)
  • Additional languages (Lua)
  • Cell reordering (drag-and-drop + move arrows)
  • Delete individual cells

Showcase Polish

Near-term items to make R and cross-language features demo-ready:

  • ggplot2 supporttheme_scirepl() dark theme, auto-applied when ggplot2 loads
  • webR download modal — Styled modal with progress phases replaces native confirm()
  • R workbook: ggplot2 showcase — Scatter, bar, density, box, and heatmap charts
  • R workbook: tidyverse data wrangling — dplyr/tidyr pipeline with Python↔R SharedVFS sharing
  • R workbook: statistics — t-test, chi-squared, regression, ANOVA with base R
  • Mobile touch targets — Larger tap areas, better contrast for file browser buttons
  • Screenshots & GIF — Visual assets showing R plots, cross-language data flow, mobile UI
  • R package pre-warming — Prompt to install ggplot2 + dplyr after R init, persists preference

Future Improvements

  • Settings menu — Auto-execute, confirm-delete, export format, auto-download, auto-switch workbook, R pre-warm, large touch targets, default language
  • Lazy kernel loading — App starts instantly; privacy consent + download confirmation on first CDN kernel use
  • tar/tar.gz export — Export packages as .tar or .tar.gz (selectable in Settings), using browser-native CompressionStream
  • Memory & Storage panel — Per-kernel WASM heap usage, storage quota breakdown, kernel unload, clear VFS/cache
  • Cell output collapse/expand — Toggle long outputs with a click, especially useful on mobile
  • Execution counterIn [N] / Out [N] numbering like Jupyter to track execution order
  • Basic tab-completion — Keyword/variable completion in the input textarea
  • Variable inspector — Panel showing current variables and types across kernels
  • Undo delete cell — Undo stack to recover accidentally deleted cells
  • Dark/light theme toggle — Light theme option for classrooms/sunlight
  • Notebook sharing via URL — Encode small notebooks as base64 URL fragments or gist links
  • Lua kernel — Fengari (Lua in WASM, ~500KB)
  • TypR kernel — Typed R superset (typr-wasm, ~1.7 MB) transpiles to R via webR
  • Capacitor WebView media query investigation@media (hover: none) and (pointer: coarse) may not trigger in Android WebView; determine cause and fix
  • Byte-level download progress — Track actual download progress via Service Worker interception or ReadableStream
  • Background package installs — Install packages/workbooks without switching to the target notebook tab; requires notebook-aware card creation

Testing

Playwright Tests

SciREPL includes Playwright tests that verify cross-cell communication (Notebook VFS) examples across all six kernels.

# Start local server
npm run serve

# Run all VFS tests
npx playwright install chromium   # first time only
node test_help_vfs_examples.mjs

WSL2 Memory Requirements

CDN kernels (Python/Pyodide, R/webR, Prolog/SWI-WASM) compile large WebAssembly modules in-browser. This requires significant memory:

Component Memory Usage
Chromium (headless) ~200–400 MB resident
Pyodide WASM compilation ~3–4 GB peak (50 MB download → JIT compile)
webR WASM compilation ~1–2 GB peak
SWI-Prolog WASM compilation ~500 MB–1 GB peak
Node.js (Playwright host) ~100–200 MB

When running tests on WSL2, the default memory and swap allocation (typically 50% of host RAM / ~1 GB swap) is often insufficient. The Linux OOM killer will terminate the browser process mid-compilation.

Recommended .wslconfig (edit C:\Users\<username>\.wslconfig, then wsl --shutdown):

[wsl2]
memory=5GB
swap=5GB
  • 5 GB RAM gives enough headroom for Chromium + one large WASM module with room for the OS
  • 5 GB swap acts as overflow when peak WASM compilation temporarily exceeds physical RAM — the kernel spills pages to swap instead of OOM-killing

Without adequate swap, Pyodide compilation reliably triggers the OOM killer at ~4 GB RSS, even with 5 GB total RAM.

Playwright + Large WASM: DOM Signaling Pattern

Standard page.evaluate() calls fail with ERR_STRING_TOO_LONG after loading large WASM modules (Pyodide, webR). This is a Node.js/Playwright limitation: the Chrome DevTools Protocol serializes the full execution context, and when WASM memory is large, the resulting message exceeds Node's maximum string size (~512 MB).

Workaround: Use page.addScriptTag() to inject code and DOM data-* attributes to pass results back:

// Instead of: const result = await page.evaluate(() => heavyWasmCall());
// Do this:
await page.addScriptTag({ content: `
    heavyWasmCall()
        .then(r => document.body.setAttribute('data-result', JSON.stringify(r)))
        .catch(e => document.body.setAttribute('data-error', e.message));
`});
await page.waitForFunction(
    () => document.body.hasAttribute('data-result') || document.body.hasAttribute('data-error'),
    { timeout: 300000, polling: 2000 }
);
const result = JSON.parse(await page.getAttribute('body', 'data-result'));

This pattern is used in test_help_vfs_examples.mjs for all CDN kernel interactions.

License

MIT License — see LICENSE

Credits

Built with: