View on GitHub


A lightweight writing lab quiz engine for node.js

Download this project as a .zip file Download this project as a tar.gz file



Quizzer is a tool for writing instruction. It is particularly suitable for:

In contrast to many commercial offerings in the online education "sphere", Quizzer is not designed to replace the instructor: it was built by an instructor, to enable more efficient and positive interaction with students over issues of grammar and style.

Quizzer is simple to install and use. A couple of highlights to encourage the reader:

Quizzer makes intensive use of multiple-choice quizzes, not as a test of knowledge or skill level, but as a lightweight means of encouraging students to engage positively with flaws in their writing. In our Academic Writing program in the Nagoya University Faculty of Law, Quizzer supports the following workflow:

  1. Students submit a short (250 to 400-word) essay once each week on an arbitrary topic.
  2. The instructor selects one poorly-written sentence from each essay, and composes a multiple-choice question consisting of the student's own sentence, two alternatives that also contain errors, and a corrected version. The essays themselves are not assessed.
  3. After one question is built from each submitted essay, students are sent personalized links to the resulting quiz.
  4. Students submit their responses, which are recorded on the Quizzer server. Students receive feedback immediately feedback on their incorrect responses.
  5. Class commenters (TAs, instructors, and other experienced writers) post short explanations of why the wrong answers were wrong.
  6. When students revisit their quiz links, they will find both the comments, and a list of classmates who gave the correct answer to the questions that they missed.
  7. Students are given an assessed, paper-based, multiple-choice supplementary mid-term and final exam, consisting entirely of questions from the quizzes.

Quiz distribution, commenting, exam composition, and marking are all managed by Quizzer. Paper tests are randomized as a hedge against cheating, and marked with a barcode reader for quick assessment. (Tip: a scanning wand is both more convenient and cheaper for this particular purpose than a laser scanner).

Using a small Markdown syntax extension, commenters can add one-line guidance notes ("rules") to their posts. Rules are made instantly available to the posting commenter in a pulldown menu, and enter a queue for promotion to system-wide use. Promoted rules can be glossed by students who perform well against them, for the benefit of their classmates from the same language domain. While student quiz responses and attached comments are typically purged at the end of each term, rules and their multilingual glosses persist, providing an evolving base of guidance notes tailored to the (preferences and the instructor and) the conventions of the target community.

The initial inspiration for Quizzer was a small code sample posted by Chetan Jain. His code has been refactored and extended considerably for this project, but I gratefully acknowledge his code as the starting point for this frolic. Hats off also to the developers of node.js, and LaTeX and, well, the developers of everything else that I used for this project. Quizzer was built on short notice to fill a critical need, and it has been a real pleasure to see how quickly the whole thing could be brought together.


The most important phase of the Quizzer cycle for teaching purposes is commenting on wrong answers. At this point, students have engaged with style requirements to produce their original submission, and engaged with one anothers' mistakes through the quizzes. Targeted feedback is the capstone that helps the class grow more confident in their next submission.

Use the Commenters page to register trusted contributors. If the setting is enabled, a commenter receives a mail message once each week, containing a personal link to the comment views. The link changes with each mailing.

In the commenter view, a list of courses opens onto a list of quizzes for each course. The quiz views show a list of wrong answers, sorted by frequency of error, and with already-commented items pushed to the bottom of the list.

Markdown syntax is recognized in comment text, with a few small extensions. To set markers in text, enclose a number or letter in double parens:

This will render as a circle-A: ((A))

For an explanation of why a wrong answer was wrong, set a single > character at the beginning of the line, followed by the pasted text of the wrong answer to be explained:

> Many new legislations were passed in 2010.

Mark text targeted for specific comment by wrapping it in double-parent, opening with a single letter or number:

> ((1 Many)) new ((2 legislations)) ((3 were)) passed in 2010.

((2)) is a non-countable noun, and is *never* written
with an 's'. With a non-countable noun, use 'much', not
'many' at ((1)), and the singular verb form at ((3)).

For a comment that states a pattern, open with two '>' characters:

>> I studied *for* two hours.

For a comment that states a grammatical rule, open with three '>' characters:

>>> Never use the word "nowadays" in formal writing.

When set at the very beginning of an entry, the explanation, pattern, and rule prefixes trigger a tidy heading in the displayed entry.

Storage and files

Quizzer data is held in an sqlite3 database, located in the directory from which the script is run, and named after the port number under which the instance is running. A companion configuration file with the extension .cfg holds the runtime parameters.

An editable template of the weekly message to be sent to commenters registered on the Quizzer instance will be written into the directory from which the script is run. Be sure to touch up the text of the message to suit your requirements. The sample template has my signature at the bottom; you can and should remove my name and replace it with your own.

When examination papers are generated, they will be placed in an exams subdirectory and Quizzer will create to house them, together with the LaTeX source of the quizzes. Quizzer offers the exam paper text to the administrator with a one-click download, so there should be no need enter the directory, but the files are there if you need them, or if you are curious about the LaTeX behind the formatting.

Basic Installation

Install quizzer from the npm repository:

npm install quizzer

In addition to the dependencies pulled in by npm, Quizzer needs to have access to the external programs pandoc and pdflatex. While pdflatex should be available as package installs on your operating system (probably as part of the texlive package), pandoc is a different matter.

Quizzer uses a simple JavaScript module to convert markdown text to HTML, supplemented by MathJax for mathematical formulae. When generating examination papers, the markdown + MathJax markup must be converted to LaTeX, and pandoc is used for that purpose. For things to work correctly, mathematical formulae must be left intact during the conversion. An extension to pandoc (tex_math_dollars) exists for this exact purpose, but unfortunately it is available only in pandoc version 1.10 and above.

Depending on your system, installing pandoc 1.10+ may be something of a chore at this point in time. In our own environment, getting it in place on an aging virtual server running CentOS 6 required a scratch build of both the Glasgow Haskell Compiler and the Haskell Platform. Everything worked swimmingly after that was done, but the compile process took several hours, and needed to be nudged forward repeatedly.

If pandoc 1.10 is not an option, and you don't need maths rendering, you can use an earlier pandoc by editing the following file:


Just change html+tex_math_dollars to simply html, and you should be good.

Apart from this transitional speed bump, installation is simple. Run the server by saving the following code to a file (such as quizServer.js, say):

var qz = require('quizzer');;

Run the script from command line like this:

node ./quizServer.js

The script will whinge on first run, asking for some essential details:

usage: quizServer.js [-h] [-v] [-H PROXY_HOSTNAME] [-p REAL_PORT]
                     [-e EMAIL_ACCOUNT] [-s SMTP_HOST]

Quizzer, a quiz server

Optional arguments:
  -h, --help            Show this help message and exit.
  -v, --version         Show program's version number and exit.
                        Host name for external access
  -p REAL_PORT, --real-port REAL_PORT
                        Port on which to listen for local connections
  -e EMAIL_ACCOUNT, --email-account EMAIL_ACCOUNT
                        Full username of email account (e.g.
  -s SMTP_HOST, --smtp-host SMTP_HOST
                        SMTP host name (e.g.
  ERROR: must set option smtp_host
  ERROR: must set option proxy_hostname
  ERROR: must set option email_account

A GMail account can be used as EMAIL_ACCOUNT, with as SMTP_HOST. REAL_PORT will default to 3498, but can be set to other values for multiple server instances. For initial testing, PROXY_HOSTNAME should be set to or localhost. Running again will yield this:

ERROR: file mypwd.txt not found: Error: ENOENT, no such file or directory './mypwd.txt'

Save the email account password to disk in a file mypwd.txt (only the user running Quizzer should have access permissions on the file, obviously). This will get the server running:

Wrote config parameters to quizzer-3498.cfg
Quizzer can now be run with the single option: -p 3498
Admin URL: http://localhost:3498/?admin=fyvg19vx
Adding admin role
Loaded class membership keys
Done. Ready to shake, rattle and roll!

Connect to the listed URL with a browser, and you're ready to go. The database and configuration files are created in the directory from which the script is run, named after the port number. The server can be shut down with CTRL-c (SIGINT), and as the startup message says, it can be restarted with the single option -p <REAL_PORT> (Note that the admin key is automatically generated, and will differ from that shown in the example above.)

Running Quizzer behind a Proxy

When PROXY_HOSTNAME is set to a fully qualified domain name (e.g., it will assume that it is being run behind a reverse proxy, and adjust URLs accordingly. Quizzer itself has only the thinnest concept of security, and should be run behind a proxy in production (and preferably over SSL). Access to the administrator display depends on a key set in the URL of a GET request. Rewrite rules on the front-end web server should be used to assure that attempts to set the key directly are rerouted through a password-protected URL.

If lighttpd is used as the front-end server, and Quizzer is run from a directory quizzer to which the server has access, configuration settings like the following should do the trick:

url.rewrite = (
  "^(?!/quizzer)(.*)\?admin=[^&]+(?:&(.*))*" => "/quizzer/admin.html$1?$2",
  "^(?!/quizzer)(.*)&admin=[^&]+(?:&(.*))*" => "/quizzer/admin.html$1&$2",
  "^/quizzer/admin.html$" => "/quizzer/admin.html?admin=fyvg19vx",
  "^/quizzer/admin.html\?(.*)$" => "/quizzer/admin.html?admin=fyvg19vx&$1"

$HTTP["host"] == "" {
  proxy.server = ( "/quizzer" => ( ( "host" => "", "port" => 3498 ) ) )

auth.backend = "htdigest"
auth.backend.htdigest.userfile = "/etc/lighttpd/lighttpd.user"

auth.require = ( "/quizzer/admin.html" =>
    "method" => "basic",
    "realm" => "Quiz Admin",
    "require" => "user=quizmaster"


The page layouts are pretty basic, but the buttons all work.

Top page

Top page

Global student list

Student list

Classes list


Class view


Quiz editing

Quiz editing

Submission stragglers

Stragglers view

Commenters admin


Commenter view



Exam sample