Best Practices
Here are some recommendations for how to build a successful application in volunteer science.
jQuery
- jQuery is a really helpful abstraction of JavaScript. It is automatically included.
FireBug or other browser debug tool.
- You are going to need some kind of tool to debug your application. There are good ones for most browsers, our favorite is FireBug for Firefox. You are going to need some kind of tool to debug your application. There are good ones for most browsers, our favorite is FireBug for Firefox.
Embedding data in a url
- You can send data about a user to a Volunteer Science experiment by embedding data in the url to your study.
For example, say you have data about the user from another study. You can create an ID number for the person and
pass that ID number to your Volunteer Science study using the following url construction formula:
https://volunteerscience.com/join_category=[your study number]&userid=[the ID]. You would simply need to add the
following to your study in Volunteer Science.
//assuming the url is something like https://volunteerscience.com/join_category=1234&userid=123456. var params = new URLSearchParams(location.search); //gets the parameters from the url. var value = params.get('userid'); //sets value=123456 submit(value) //save the value to your output data so you can link the subject
You can use this same approach to categorize users for different versions of your game. For example, if you know your participants are Spanish-speaking, you can put that in the url and then only show Spanish-language texts.//assuming the url is something like https://volunteerscience.com/join_category=1234&lang=sp var params = new URLSearchParams(location.search); //gets the parameters from the url. var value = params.get('userid'); //sets value=sp; if (value=='sp'){ alert("hola!"); }
Round Completion
- Many experiments wait for all players to submit a move before continuing to the next round. It is important to get this code right. Here's how we do this in Wildcat Wells:
function newMove(playerId, idx, round) { // there was a new move posted in this round; fetch it fetchMove(playerId, round, idx, function(val, participant, round, index) { // record the move for this round submissions[round][getNetworkId(playerId)] = JSON.parse(val); // see if all the players have submitted a move for (var i = 1; i <= numPlayers; i++) { if (!(i in submissions[currentRound])) { return; // no, do nothing } } completeRound(); // proceed to the next round }); }
Timeout
- It's possible that the user is taking a long time to think or has walked away from their computer. It's important to make sure to eventually force a "null" move for such a case.
The general policy should look something like this:
var userTimeout = null; function initializeRound() { ... // set up the user interface userTimeout = setTimeout(submitMyMove, 30000); // automatically call submitMyMove() in 30 seconds } function submitMyMove() { if (userTimeout != null) { // cancel the existing timeout so the move isn't submitted twice clearTimeout(userTimeout); userTimeout = null; } submit("my move"); // generate this from the user's actions, or handle if they haven't automatically called submit. }
Typically you also want to display the time remaining and update the clock every second. We have a helper script calledcountdown.js
(TODO: document and make available). Math.seedRandom()
- Occasionally you will need your game to make random decisions.
Rather than attempt to synchronize this initialization data across the browsers,
it is easier to seed all the browsers with the same random number generator then call it identically.
Sadly, JavaScript doesn't ship with this capability, so we regularly use seedrandom.js to handle this. The first step is to add seedrandom.js to your application. Do this in the "Files" section of your experiment.
The second step is to callMath.seedrandom(seed)
before any series of calls to the random number generator. Here is an excerpt from the TSP experiment:function initialize() { // seed all clients the same Math.seedrandom(seed); // choose how often we see other player's results var info = Math.floor(Math.random()*3); ... // whether we display the user's last or best result var best = Math.floor(Math.random()*2); ... // which kind of bot to use botType = Math.floor(Math.random()*2); ... }
If you need to make additional random decisions across all clients later, you have to be careful not to "contaminate" the random source by calling Math.random() differently on different clients. The easiest thing to do is to re-seed the random number generator later. We suggest using some mathematical combination of the seed and the currentRound:function doSomethingRandom() { Math.seedrandom(seed*7 + currentRound*13); ... }
Player Disconnect and Bots
- If your experiment is multi-player, sometimes a user will drop out of the experiment.
Even if this invalidates your results, it is critical to maintain a positive user experience for the remaining players.
Volunteer Science has a multi-pronged system to detect if a user is still active. When it detects a dropped user, we call playerDisconnect(playerNum).
We recommend using the lowest remaining playerNum to take responsibility for all bots and dropped players. Here's how we handle this in Wildcat Wells:
function playerDisconnect(playerNum) { if (playerId < myid) { submitRemainingBots(); } } /** * also called when a player submits their choice */ function submitRemainingBots() { // abort if I'm not the lowest active player for (var i = 1; i < myid; i++) { if (activePlayers[i]) return; } for (var i = 1; i <= numPlayers; i++) { if (!activePlayers[i]) { // dropped player doBotBehavior(i); // submit a value } } }
Interactive Instructions
- Though this can require significant development time, the best user experience comes from having interactive instructions or practice rounds. This often involves setting the first real round to be #101
Secret Award (Skip Instructions)
- You may want to force the user to go through the instructions until they can successfully complete the task.
However, once they have played the game a few times, the instructions may become annoying.
We solve this by giving the user a "secret" or "non-visible" award that allows them to skip instructions.
This code from "Word Morph" shows the "Skip Instructions" button on successive games, after the user has complete the instructions the first time.
function initialize() { // show the "Skip Instructions" button only if they've completed them before try { if (awards[myid]["instructions"]["count"] > 0) { $("#skip_instructions").show(); } } catch(err) { } } /** * called the first time the user successfully completes the instructions. */ function instructionsComplete() { writeAward("instructions",0); }
Embedded Video
- Deploying videos takes an entirely different type of distribution infrastructure than Volunteer Science provides. Please use YouTube, Vimeo or a similar service to include an embedded video in your experiment. Each of these services has its own documentation on how to include and start the video.
Amazon Mechanical Turk Preview Mode
- Amazon Mechanical Turk allows workers to "preview" an experiment before actually playing it.
Volunteer Science detects this and puts them into a 1-player game even if this is not a valid condition for your experiment.
We recommend checking
IS_AMT_PREVIEW
and only displaying the instructions or video during this phase.