Intro to Problem Authoring

In this post, we’re going to go over everything you need to know in order to write your first WeBWorK problem. Because of the way text is formatted on this website, please do not copy-paste any code directly from this page.

Coding your first WeBWorK problem:

Every WeBWorK problem begins with a section that looks like:


## PG document structure ## ENDDESCRIPTION

## KEYWORDS(’sample’, ’WeBWorK’)

## DBsubject(’Subject’)

## DBchapter(’Chapter’)

## DBsection(’Section’)

## Date(’today’s date’)

## Author(’Your name here’)

## Institution(’Your College’)

## TitleText1(’’)

## EditionText1(’’)

## AuthorText1(’’)

## Section1(’’)

## Problem1(’’)

This section is here to help the problem get categorized properly if/when it is submitted to the Open Problem Library (OPL), and so that your institution can find homebrewed problems using the Problem Library Browser. It might seem like a pain to fill this section out for every problem you write, BUT it’s much worse to have to go back and fill it in for dozens or even hundreds of problems that you’ve already written when you’ve ignored this section on every single one. Read this page: for more information, and links to current keywords.

A current version of the WeBWorK Subject/Chapter/Section breakdowns can be found here:

The next chunk of code will also be mandatory:







Both and are mandatory macros that need to be loaded for EVERY problem. You will find other macros necessary from time to time, especially in those circumstances where you might want to require students to enter their answers in a particular way. will be a great resource to help you get comfortable with the options currently offered by WeBWorK. We’ll continue to mention these other macros as we move through this document.

TEXT( beginproblem() ); tells WeBWorK that we’re ready to begin the problem. This is also mandatory code for every problem.

What follows will be our code for a sample problem:


$r = random(3,9,1);

$answer = Compute(“pi * $rˆ2”); describes the settings controlled by the Context() call and how to change them. lists the available contexts that are automatically available with

Contexts tell WeBWorK what kind of problem we’re working with. They are very powerful, and there are lots of options available within each possible context that we might use. provides us with a list of the available specialized contexts we are able to use in a problem. (Note that each specialized context has an associated “pl” file that must be loaded in the loadMacros() call above in order to let WeBWorK know ahead of time that we’re planning to use a non-standard context.) Contexts range from dealing with simple things like currency or fractions to more complicated problems using vectors or piecewise functions. Setting your context properly will help WeBWorK display your problems more accurately and efficiently, and will make the evaluation process smoother for students – usually offering more helpful error messages when students don’t input answers properly. Because we’re writing a simple WeBWorK problem here at first, nothing more exotic than the “Numeric” context will be required.

The “random()” function call is a staple of nearly every WeBWorK problem. The arguments for the random function are as follows:

$foo = random(min,max,interval);

Usually, an interval of 1 is used with an integer minimum so that all random results are integers.

Defining an answer variable ($answer, here, can actually be named anything, but for clarity – answers are usually given a name that indicates their role in the problem.) is also a necessary part of every WeBWorK problem.

You’ll note that nothing that we’ve written so far will be visible by students. Everything so far is going on behind the scenes – but that’s all about to change. We’ve generated a randomized $r variable, and we’ve also computed an answer variable, $answer. We’ve got everything we need in order to ask our question and evaluate the students’ responses.



What is the area of a circle of radius \($r\)?


Area = \{ ans_rule(10) \}



In preparation for the problem as we want students to see when they attempt this problem, we tell WeBWorK to stop treating what we write as code, and start formatting it for display [ Context()->texStrings; ] This is followed by the BEGIN_TEXT command that acts kind of like clicking “OK” when you computer asks “Are you sure?”

We ask our question, “What’s the area of a circle of radius \($r\)?” And you’ll notice a couple things. One, $r is our randomized parameter that we defined above (and it will be displayed as the number for which it stands). Two, we’ve got it surrounded by \( and \). These pairs of symbols act as LaTeX delimiters for math mode – just as $…$ do in actual TeX code. However, in Perl, $ is a reference to a variable and is not available for use to trigger TeX formatting. Moreover, if you ever want to display the actual $-sign in a problem, you’ll need to escape it first like so: \$. If you want to use displaystyle TeX formatting (usually seen as $$…$$) you’ll want to use the \[ … \] delimiters.

$BR is a WeBWorK specific shortcut for line-break. $PAR acts like a 1.5-line-break. Use them often. WeBWorK will ignore the breaks that you put between your text unless you specifically use $BR or $PAR.

Moving on, we then ask for an answer from the students: “Area = \{ ans_rule(10) \}“. In this line, the \{ … \} delimiters allow us to call Perl code from within the section where we’ve told WeBWorK we weren’t writing any code. The code we’re calling here is the code to request an answer box for the student. The “10” sets the width for the student response box.

Finally we close our displayed problem with END_TEXT and tell WeBWorK that we’re going back to code: “Context()->normalStrings;

$showPartialCorrectAnswers = 1;

ANS( $answer->cmp() );

Now that we’re back to coding, we need to evaluate the students’ answers. “$showPartialCorrectAnswers = 1;” is commonplace, as problems often ask students to enter more than one response – and if they don’t get ALL parts right, we usually want them to know which ones need fixing. On the other hand, you might want to set this variable to “0” in cases where you’re asking several multiple choice questions (no multiple-guess problems) or if you’re asking multiple T/F questions.

The second line here gets used ALL THE TIME. “ANS( $answer->cmp() );” tells WeBWorK to compare the student’s response to the variable “$answer” that we already defined above. In multi-part questions, you might consider naming your answer variables “$ans1”, “$ans2”, “$ans3”, etc. in order to keep them all separate and easily followed.

The following is OPTIONAL, but RECOMMENDED:

WeBWorK supports the inclusion of a solution for each problem. These solutions are made available according to the dates set alongside the open and due dates in the Problem Sets Editor. These solutions can be very helpful to students when they’re reviewing for exams, especially when combined with the “Show Me Another” feature.



The formula for the area of a circle of radius \(r\) is \(\pi rˆ2\), so the answer is \(\pi \cdot $r^2 = $answer\).




You should recognize the two lines beginning with “Context()->” as they are exactly the same as what we used before to tell WeBWorK to stop looking for code, and start looking for stuff to display. Likewise, BEGIN_SOLUTION and END_SOLUTION are pretty self-explanatory. And the line that describes how the problem should be done is the solution that will be provided to students after the solution date.

NOTE: “ENDDOCUMENT();” MUST be the last line of EVERY WeBWorK problem.

What happens when it doesn’t work?

At first, the problems you write will probably fail to work several times before you get it exactly right. Here’s some common things to look out for:

  • Does every line of code (not between the BEGIN_ and END_TEXT) end with a semicolon? (Note: Sometimes, especially with loops and setting context parameters, line breaks will be used to make the code look cleaner, but the end of the line isn’t necessarily the end of the line of code.)
  • Does every begin quotes have an end quotes?
  • Does every open parenthesis have a partnering close parenthesis?
  • Make sure you use your delimiters in pairs: \(…\), \[…\], \{…\}
  • Do you have an equal number of ANS( $ans->cmp() ); calls and \{ ans_rule() \} calls?
  • Look at the error message. It will usually point to a line in your code. That may not exactly be the right line, but you will have an error somewhere BEFORE that.

Best Practices

Write a problem and code it without using any randomized variables. For any parameters that you would want to have randomized, assign a variable to replace that number, and calculate the answer variable using your variables (and any other constants that you may not be randomizing). Finish writing the problem and see if you can get it to display the way you want it. Once that’s done, replace your fixed parameter variable assignments with randomized ones and see if it still works.

Or, write a problem and start by randomly defining the answers. Code up the parameters necessary in the problem statement from the pre-defined random answer variables. Once the problem displays properly, attempt several different versions of the problem to confirm that the problem does in fact give the expected answers.

Write down a problem where every constant value is replaced by a variable parameter. (Like you would for computing the quadratic formula from an arbitrary quadratic equation.) Solve the problem using these variable parameters, making note of any constraints you’d like to place on these parameters in order to ensure a “clean” solution.

Work with a partner. Have your partner solve your problems and play the same role for them. Make sure to try at least two or three different seeds for each problem.

Look in the OPL for problems similar to the ones you’re trying to create. It’s not always easy to read others’ coded problems, but doing so can not only build upon your own repertoire, but you might find the very problem you were looking to write. Feel free to re-word any problem you find on the OPL and save a NEW VERSION of it. Put the original problem “path/” at the end of the problem, just before the ENDDOCUMENT(); using the COMMENT(“…”); code, so that you give credit back to the original authors. You can edit more than just the wording, but often times, it’s better to write your own code from scratch – it can be hard enough to READ their code much less EDIT it.

Stick to the tutorials that use MathObjects instead of using PGML.

COMMENT YOUR CODE. Seriously. Please comment your code. Especially in any spots where you had difficulty in figuring out how to make things work. This is a huge benefit for YOU when you run into this problem again later, as well as for EVERYONE ELSE if they ever need to edit your problem. Commenting is as easy as starting a new line with the # symbol and then typing whatever you want. WeBWorK will ignore anything that comes after a #. You can even put a # after your semicolon to end a line of code and follow it with a short bit of commentary.


PDF Guide:

Trunk for lots of resources:

WeBWorK Forum:

IRC Chat:

2 thoughts on “Intro to Problem Authoring”

  1. Hi Dr. Parker,
    This website is very useful for beginners. Do you have any information of how to change permission level for different roles? For instance, is there a way to let TA be able to grade for students, but not able to use the hmwk set editor.


    1. I’ll have to look into that. You are already able to set a student’s permission level to TA, yes? And what you’re asking about is setting the actual permissions for a user that is classified as a TA?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.