The Ursinus WebIDE
Christopher Tralie and William Mongan
As a response to emergency remote teaching during the pandemic, we developed an online “live coding” modules system back in 2020, where students write code in a web page where they can run their code, and it is automatically graded. We have been using this extensively across the entire CS curriculum ever since as a “low stakes” medium to teach and reinforce knowledge scaffold up to more difficult assignments. Since the modules run in the browser, they are highly accessible cross-platform without any software installation necessary. The system is also setup to be “client side” on static web pages, which means that the code the students are exploring runs on student’s computers, making the system much easier and cheaper to maintain; in fact, this even enables us to make the exercises available to anyone on the internet at no additional cost.
Our system addresses the following goals that we had in providing rapidly developed, low-stakes, frequent practice problems for our students, that have continued to enhance the student experience in the post-pandemic classroom:
- Create a unified code template across all languages we currently support (C++, Java, Javascript, WebGL, Python, Numpy, R, SQL).
- Remember students netid in a cookie so they don’t have to keep entering it, and add functionality to save/load code if they want to return to it.
- Devise a simpler way to communicate with Canvas that is easier to maintain.
- Setup a more intuitive file browser in the module web pages that shows students what files they can edit for each module. Mirror the filesystem experience they would expect on their local computers to help scaffold a tree-based filesystem.
- Handle infinite loops that students accidentally create in a more graceful way (currently this freezes up the browser).
- Provide the illusion of a filesystem within the web development interface that provides students with experience and confidence in using modern development tools and non-mobile filesystems.
- Seamlessly display multimedia assets within the environment, such as images and audio, to support courses that entail multimedia development and processing.
- Provide low-stakes automated feedback to students by automatically detecting common mistakes and providing feedback in a more authentic “console-like” window
- Save student work within the browser, so that students can review their work at a later time by revisiting the exercise page.
- Perform all of the above without requiring student installation of software beyond an off-the-shelf web browser, regardless of operating system or computing environment constraints; specifically, software modules function on lower-end systems such as Chromebooks. This is especially helpful as an equity assurance for students who add the class late and need time to install requisite software, and for students who suffer technology breakdowns during the semester.
- Provide an archive of and automatic grading of student submissions that can connect to learning management systems (LMS) in an encrypted, FERPA-complaint way.
These modules engage students and get them to think about topics outside of class in a much more active way than if we simply assigned readings, particularly with instant feedback we give them that often includes hints relevant to the mistakes they make. The modules are also very dynamic and flexible, so there are lots of exercises we can do with multimedia, etc. The infrastructure also serves as a backup option the first couple of weeks of class while students are still getting software setup on their own computers. Finally, there is a subset of students who have consistently told us that they get more out of these than class because they can go through them at their own pace, and we often use them to keep students on track who are traveling for sports, etc. Since these are freely available open source online, anyone on the internet can try them. An example of the student interface is shown below:

Student-facing features
The IDE layout (_layouts/exercise.html) ships a number of UX, accessibility,
and debugging helpers that work on every supported language. None of them
require any change to existing exercise pages — they appear automatically.
Preferences (View menu)
Open View in the menu bar to access three preferences. All choices persist
across reloads in localStorage under the key webide.prefs.
| Item | What it does |
|---|---|
| Theme | Cycles Dark → Light → High Contrast → Dark…. The light theme reflows the entire IDE — including ACE syntax tokens — to a light palette. High Contrast is a low-vision-friendly black/white/gold scheme with gold focus rings. |
| Font Size | Cycles through 11, 12, 13, 14, 16, 18, 20, 24 px and re-applies to every ACE editor instance. You can also press Ctrl+= to increase and Ctrl+- to decrease (each press persists). |
| Reading Mode | Toggles a Dyslexia-Friendly mode that prefers the OpenDyslexic / Atkinson Hyperlegible fonts (with a Comic-Sans/Verdana fallback so it works offline), increases letter-spacing, word-spacing, and line-height in editor + UI text. |
Keyboard-shortcut overlay (?)
Press ? (or pick Help → Keyboard Shortcuts) to open a modal listing every
shortcut. Esc closes it. The overlay is focus-trapped for keyboard users and
returns focus to wherever you came from. Inside text fields, ? types a literal
question mark — press Esc first if you want to summon the overlay.
Responsive / compact layout
Below ~900 px viewport (e.g. a student reading the exercise on their phone), the IDE collapses gracefully: the activity bar is hidden, the sidebar narrows, keyboard-shortcut hints are dropped, and the bottom panel grows to fill the remaining height.
Accessibility pass
Every clickable button has an aria-label, tab/panel pairs use the ARIA
tablist pattern, the editor exposes role="group" with a screen-reader-mode
ACE option, and all keyboard-driven focus is outlined in a gold focus
ring via :focus-visible — mouse clicks don’t produce outline noise.
“Explain this error” affordance
Whenever student code raises a recognizable runtime error, the line printed to
the Output panel gets an inline ? What does this mean? button. Clicking it
expands a plain-English explanation drawn from a per-language dictionary
covering common Python, Pyodide, JavaScript, Java, C++, SQL, Scheme and Prolog
errors (e.g. NameError, IndexError, NullPointerException,
ArrayIndexOutOfBoundsException, segmentation fault, undefined reference,
no such table, Unbound variable, …). The same explanation is also appended
to the Suggestions tab so it remains discoverable after the console scrolls.
Infinite-loop watchdog
Every Run click immediately writes a Running… banner to the Output panel explaining how to recover if the tab freezes:
Running… If the page freezes for more than ~10 seconds, your code may have an infinite loop. Your file work is saved automatically — refresh the tab (Ctrl/Cmd+R) to recover.
For runtimes that yield to the event loop (worker-based C++, async paths), a
5-second toast and a 15-second console message with a Reload page button
also fire. For runtimes that block the main thread (Brython exec,
JavaScript eval), the upfront banner is the only reliable signal — which is
why it appears before the run starts.
Inspector (bottom-panel tab)
A new Inspector tab next to Terminal / Output / Suggestions hosts three sub-views, populated automatically after every Run:
- Variables — top-level variables defined by the student’s code, with
name, type, and a truncated repr/value.
- Python (Brython): captured via an injected
locals()snapshot postlude. - Pyodide: walked from
pyodide.globalsafter the run. - JavaScript: computed by diffing
windowkeys before and aftereval. - Other languages: shows a friendly empty-state pointing out that variable inspection is currently Python/JavaScript-only.
- Python (Brython): captured via an injected
- Step-Through — record execution one line at a time with
sys.settrace, then play back with ⏮ ◀ ▶ ⏭ controls. The current line is highlighted in the ACE editor and locals at that step appear side-by-side.- Click ↻ Step Run in the Inspector toolbar to opt into the traced runner for one run (the normal Run button does NOT install the tracer, to keep regular runs fast).
- Pyodide (full CPython) supports stepping completely.
- Brython has partial
sys.settracesupport; if no events were captured, a friendly empty-state explains it.
- Call Tape — a tree of function calls with arguments and return values.
Recursion produces nested branches; sequential calls produce siblings;
exceptions get a ⚡ marker.
- Python (Step Run): recorded automatically via settrace.
- JavaScript / Java: students add markers themselves. Two helpers
are exposed on
window:webideTrace.tap('name', arg1, arg2)— adds a flat sibling entry. Best when you just want to log “did this code run?”.webideTrace.call('name', args…)placed as the first line inside a function body, paired withreturn webideTrace.return(value)immediately before every return statement — produces a nested recursion tree. Mismatched pairs are flagged inline with(no .return — try webideTrace.tap() for sibling logging)and the summary line counts unclosed frames.
Other helpers also exposed on window for in-browser debugging:
window.logToConsole(msg), window.refreshInspectorFromRun(),
window.menuCycleTheme(), window.menuShowShortcuts().
Test suite
All of the above is exercised by a Playwright-driven test suite in tests/
that runs in GitHub Actions on every push and PR. See
tests/README.md for the per-language coverage matrix
and the local instructions.
Installation Instructions
- Fork this repository, and set up Github pages to get a URL.
- To get started with our pre-made assignment exercises across several undergraduate CS courses (available at https://github.com/BillJr99/Ursinus-Exercises), simply add and clone our submodule of exercises into your
_pagesdirectory viagit submodule add https://github.com/BillJr99/Ursinus-Exercises _pages/Exercises. - If you would like to use the Computer Graphics assignments, you can add an additional submodule into the
assets/js/ggslacdirectory viagit submodule add https://github.com/ctralie/ggslac.git assets/js/ggslac. - Proceed to the Configuration section below to set a few global parameters for linking to your Learning Management System (LMS)
- Fork and/or Download the Form Processor (https://github.com/BillJr99/formprocessor/) to configure a Google form/spreadsheet to receive student submissions from the WebIDE front-end, and to post grades and submissions to your LMS in a FERPA-compliant way. Instructions for configuring the Form Processor can be found on that repository.
- Create assignments in the
_pages/directory (details about how to do this are provided below). We encourage you to submit pull requests back to the Ursinus-Exercises git repository to share your work with others!
Basic Configuration
In _config.yml, edit the variables to suit your preferences. Specifically, be sure to set the following parameters:
publickey: |
YOUR PUBLIC KEY HERE
canvascourseid: courseid1, courseid2, courseid3
formlink: LINK TO GOOGLE SHEET FOR FORM PROCESSING HERE
If desired, these variables can be overrided on a per-assignment or per-page basis from the global values configured here:
publickeycan be overridden in the layoutcanvascourseidcan be overridden in the exercise pageformlinkcan be overriden in the layout
You can generate a public and private RSA key for encryption via these commands:
openssl genrsa -out private-key.pem 4096
openssl rsa -in private-key.pem -pubout -out public-key.pem
And paste the contents of the relevant file when requested in the config file.
Backend Form Processor for Posting Grades
Use this WebIDE front-end with the formprocessor back-end (available at https://github.com/BillJr99/formprocessor/) to pull from google sheets
Example Exercises
This repository contains examples for various programming languages. Each example is linked below, organized by language.
A Gallery of Example Screenshots

CS1 and CS2
Java
- CS173: Intro to Computer Science - ArrayLists (source)
- CS173: Intro to Computer Science - Nearest Value in an Array without Going Over (source)
- CS173: Intro to Computer Science - Introduction to Boolean Expressions (source)
- CS173: Intro to Computer Science - Introduction to Conditionals (source)
- CS173: Intro to Computer Science - Dynamic Programming (source)
- CS173: Intro to Computer Science - Epoch Time Overflow Calculator (source)
- CS173: Intro to Computer Science - Introduction to Primitive Data Types and Expressions (source)
- CS173: Intro to Computer Science - Introduction to Primitive Data Types and Expressions Revisited (source)
- CS173: Intro to Computer Science - Four in a Row (source)
- CS173: Intro to Computer Science - Functions (source)
- CS173: Intro to Computer Science - Introduction to the IDE (source)
- CS173: Intro to Computer Science - Insertion Sort (source)
- CS173: Intro to Computer Science - Introduction to Iteration (source)
- CS173: Intro to Computer Science - Iteration Revisited (source)
- CS173: Intro to Computer Science - Merge Sort (source)
- CS173: Intro to Computer Science - Recursion (source)
- CS173: Intro to Computer Science - Strings (source)
- CS173: Intro to Computer Science - Strings Revisited (source)
- CS173: Intro to Computer Science - Tic-Tac-Toe (source)
- CS174: OOP - Drills - Array 3 Sort (source)
- CS174: OOP - Drills - Array Insert (source)
- CS174: OOP - Drills - Computing the mean of arrays (source)
- CS174: OOP - Drills - Array Min Index (source)
- CS174: OOP - Drills - Creating the reverse of an array (source)
- CS174: OOP - Drills - Printing array with commas (source)
- CS174: OOP - Drills - Counting Array Zeroes (source)
- CS174: OOP - Drills - Vowel Counting (source)
C++
- C++ Basics (source)
- CS174: Module 14: Recursive Binary Search (source)
- CS174: Module 12: C++ Inheritance Exercise 1 (source)
- CS174: Linked List: addFirst() (source)
- CS174: Merge Sort Exercise (source)
- CS174: Pointer Fundamentals Module: Exercise 1 (source)
- CS174: Module 12: C++ Inheritance Exercise 2 (source)
- CS174: STL List Class Exercise (source)
- CS174: Module 15: Binary Trees Inorder Traversal (source)
Javascript
Python
- CS377: Database Design - Iteration in Python (source)
- CS377: Database Design - Quadratic Formula in Python (source)
- CS 271: Module 1: Python Basics Part 2 (source)
- CS 372: Module 2: Square Wave (source)
- CS 477: Module 1: Numpy Exercise (source)
Principles of Programming Languages
Prolog
- CS374: Principles of Programming Languages - Warmup with Prolog (source)
- CS374: Principles of Programming Languages - Warmup with Prolog Part 2 (source)
Scheme
Statistics
R
- CS173: Intro to Computer Science - R (from WebR) (source)
- CS173: Intro to Computer Science - R Quadratic Equation (from WebR) (source)
Database Systems with SQL
- CS377: Database Design - SQL Aggregation (source)
- CS377: Database Design - SQL Joins (source)
- CS377: Database Design - Introduction to Databases (source)
Computer Graphics
- CS 476: Computer Graphics - Module 10 Exercise 1 (source)
- CS 476: Computer Graphics - Object First Perspective/Viewing Exercise 1 (source)
Assignment Language Specific Notes
Pyodide Modules
In the markdown files for the modules you’re creating, be sure to specify the packages field nested under info to load the packages that are necessary for the module. For instance, for a module that creates a plot in matplotlib, you’d say
packages: "numpy,matplotlib"
After creating a plot, use the built-in method save_figure_js() in your main code in python to save the figure to the figure area. You can also then look at the string audioStr in the command line to extract the base64 binary code for the image to create reference solutions
When creating an audio module, use the built-in method
save_audio_js(y, sr)
which takes in an array of samples and a sample rate. Then check the global string audioStr for the base64 binary to create reference solutions
Graphics Modules
The graphics modules are a bit complicated. My recommendation is to look at the examples exercise-shader-lambertian and exercise-view-orthographic for examples, and to build off of those examples.
As with the pyodide plots, you can do correctness checks by comparing a base64 encoded version of what’s rendered to some reference base64 string. The current output is in the variable canvasStr. So, for example, if I wanted to compare the current output to some string called correctStr, then I could say the following in feedbackprocess
let isCorrect = await pngImagesEqualTol(canvasStr, correctStr, DEFAULT_TOL, "correctCheck");
Then, my correctcheck could simply be
correctcheck: isCorrect
One thing that’s different here from a direct comparison is that, due to slight variations in graphics hardware, the results will be slightly different pixel to pixel. Therefore, the method pngImagesEqualTol actually does a pixel by pixel Euclidean distance calculation between the two images, and it will return true as long as the distance is less than some tolerance (specified as the third argument). You can change this tolerance, but I’ve provided a value DEFAULT_TOL that I found worked well for students in the past.
Other notes:
Be sure that the relative paths for all meshes in assets/js/ggslac/meshes are correct based on how deep the module is. For example, in the homer mesh in exercise1-orthographic, we say
"filename":"../../assets/js/ggslac/meshes/homer.off"
since that module is in /Modules/Graphics/OrthographicView
Creating Your Own Exercises
To create an exercise, create a Markdown page in your _pages directory using the following template. Items you fill in are denoted with the << and >> characters (which you should remove when filling in your desired data!). Replace the other items to your preferences.
---
layout: exercise
permalink: <<your relative URL here - for example, /Modules/ArrayLists/Exercise>>
title: "<<title here - for example, CS173: Intro to Computer Science - ArrayLists>>"
language: "<<language here - one of the follwoing: java, javascript, cpp, python, pyodide, sql, scheme, prolog, graphics_view, grpahics_shader>>"
info:
points: 3
instructions: "Modify the Driver.java file to create an <code>ArrayList</code> containing all prime numbers between 0 and <code>n</code>."
goals:
- "To create and manipulate an <code>ArrayList</code>"
- "To iterate over an <code>ArrayList</code>"
canvasasmtid: "<<Your corresponding Canvas gradebook assignment ID: for example, 137462>>"
canvaspoints: 3
processor:
correctfeedback: "Correct!!"
incorrectfeedback: "Try again"
submitformlink: false
feedbackprocess: |
<<instructions on how to parse the console response for correctness - for example, let ans = feedbackString.split("-");>>
correctcheck: |
<<a boolean check for correctness suitable for use in an if statement - for example, ans[0] === "7" && ans[1] === "25">>
<<optionally, add one or more checks for common incorrect answers with targeted hints and feedback>>
incorrectchecks:
- incorrectcheck: |
ans.length == 1
feedback: "Try again: you've found one of the prime numbers, keep iterating with your loop to find the other!"
<<your files and student code appear below, which will populate a file tree. One file should be main, and instructor files can be marked read-only or even invisible.>>
files:
- filename: "PrimeArray.java"
name: primearray
ismain: false
isreadonly: false
isvisible: true
code: |
import java.util.ArrayList;
public class PrimeArray {
public static boolean isPrime(int val) {
for(int i = 2; i <= Math.sqrt(val); i++) {
if(val % i == 0) {
return false;
}
}
return true;
}
public static ArrayList<Integer> buildArrayOfPrimes(int n) {
/* TODO: Create an ArrayList of Integer values */
/* TODO: iterate from 2 to n */
/* TODO: if each number is prime, add it to the ArrayList */
/* TODO: return the ArrayList */
}
}
- filename: "Driver.java"
name: driver
ismain: false
isreadonly: true
isvisible: true
code: |
public class Driver {
public static void main(String[] args) {
ArrayList<Integer> primes = PrimeArray.buildArrayOfPrimes(100);
System.out.print(primes.get(3) + "-" + primes.size());
}
}
- filename: "Excerpt from Main.java: body of main() function"
ismain: true
name: main
isreadonly: true
isvisible: false
code: |
Driver.main(null);
openFilesOnLoad: ["PrimeArray.java"]
---