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:
Installation Instructions
- Fork this repository, and set up Github pages to get a URL.
- Create assignments in the
_pages/
directory (details about how to do this are provided below). To get started, simply add and clone our submodule of exercises into your_pages
directory viagit submodule add https://github.com/BillJr99/Ursinus-Exercises _pages/Exercises
to use our pre-made exercises available at https://github.com/BillJr99/Ursinus-Exercises! - 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.
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:
publickey
can be overridden in the layoutcanvascourseid
can be overridden in the exercise pageformlink
can be overriden in the layout
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"]
---