Part 2: Configuring the Experiment
Configuring the task page
The main file that determines what is displayed to the participant (i.e., the user-side) during a game is located at /client/game/Round.jsx
.
By default, this is divided into two main components, Task
and SocialExposure
. The Task
itself is composed of TaskStimulus
which contains the stimulus (e.g., a survey question — or in this example, an estimation task) and TaskResponse
which contains the input for users to response to the stimulus.
Let's start by adding a title to the /client/game/Task.jx
so that the component looks like this:
Adding content to the task stimulus
React components have a this.props
object which contains elements passed into it. These props can be extracted with destructuring (the const {} = this.props
part). Notice that the props here include round
, stage
, and player
. These are your interface with Empirica, and allow you to both read and set data for the state of the experiment.
All we're doing here is adding an image and question to the client/game/TaskStimulus.jsx
so that it looks like this:
Adding the candy jar image
You can download the candy jar image we use here.
Then, in the public
directory, create the following directories: /experiment/images/
and add the candy jar image there so that it matches the path we just set: "/experiment/images/candies.jpg"
.
The public
directory allows you to store static assets, such as images, that can then be accessed on the client side.
Customizing the input field
In this section, we are going to change the input type from a slider to a numeric input box. To do so, go to the client/game/TaskResponse.jsx
file.
You can see that there are some "handle" functions (they don't need to be named this way, but clear names are helpful) that will execute on certain events.
There are also different "render" functions that will be called further down in the main render
based on different conditions (e.g., if the player has already submitted for this stage).
Change the renderSlider
function. First let's change it's name to renderInput
. Then, replace the slider with a numeric input box. Because we're asking people to estimate the number of candies in a jar, we'll set the minimum value to one.
We recommend adding additional form verification methods beyond the default to ensure a smooth user experience. Note that if you do choose to use a slider input, we strongly recommend using the Empirica slider, which is made with experimenters in mind — no default values that might create anchoring effects.
Notice that this method uses the player
data to control the form input. This player data is created with player.round.set()
in the handleChange
method which is called every time the input is updated. This method will save this data to the database on the server, automagically.
The sliders and inputs don't send exactly the same data format, so we have to change the handleChange
function to look like this:
Because we changed the name of the "render" function to renderInput
we need to change this in the main render
; instead of calling this.renderSlider()
it should be called this.renderInput()
.
Careful with the brackets!
You might notice that a react component, like TaskResponse.jsx
has a few import lines at the top of the file and then starts with this type of line and opening of curly brackets {}
This curly bracket will close on the last line of the file.
You will also notice that the main render of a component also opens curly brackets {} which close before those of the overall component.
So when you are changing the render of a component, careful not to delete the final bracket that closes the component, otherwise you will get an error such as '}' expected
.
Overall, TaskResponse.jsx
should look like this:
Configuring stimulus content
The next step is to allow the experimenter to modify the stimulus without having to update the experiment code directly. We'll accomplish this by adding a JavaScript object to a constants.js
file (the name of the file in itself is not important, as long as you are consistent), that will contain all the stimulus information, and then use that in the experiment via the Empirica.gameInit
method.
This all happens server-side, so you need to create this file in your server/
directory instead of your client/
directory. Create a file server/constants.js
, and add the following code to it.
The export keyword allows us to access this JavaScript object in another .js
or .jsx
file.
Using data from constants.js
constants.js
Next, we need to use this data in the Empirica.gameInit
callback in /server/main.js
. This is an important event that sets up the rounds and stages of a game.
For more about the life cycle of an Empirica app and its callback, consult our guide.
You can see that this file has a few imports and then most of the code is within Empirica.gameInit
. In gameInit, you can see that it is going through each player and assigns them some data such as a score and an avatar. Then it is doing something 10 times (using _times()
from underscore.js; a library of convenient JavaScript tools): It is creating 10 rounds, each with one stage.
First, we import the constants.js
data with import { taskData } from "./constants";
added to at the top of server/main.js
.
We are going to create a network of players. We prepared a node for each player based on their position, and then we assign each player a set of "neighbors": Every possible node except a player's own node. This means this is a full network. Of course, you could create a different type of network if you wanted to. The neighbours will see each other's responses in a social stage we are going to create.
Replace the code in your server/main.js
with the following code:
An important part of what is being done is here is adding a data
object to the rounds. Any value in this object can then be access with round.get()
.
Incorporating dynamic round data in TaskStimulus.jsx
Finally, we're ready to incorporate our new configurable task data into TaskStimulus.jsx
All we do is change the value of the constants to pull dynamically with round.get()
instead of setting them manually. Replace the lines that declare the imagePath and the questionText with these:
We also add logic so that we only display an image if a path is given:
Overall, it should look like this:
Calculating player score with callbacks
Empirica provides a set of methods that will run at the start and end of each round and stage. These can be found in server/callbacks.js
.
To learn more about callbacks, see our guide on the life cycle of an Empirica experiment.
By default, the score is equal to the total sum of responses, but this is not very informative. We'll modify this to show percentage of error subtracted from 1. Replace the Empirica.onRoundEnd
code in server/callbacks.js
with this:
These scores are compared to the correctAnswer
from our constants.js
file.
Last updated