Wait pages are necessary when one player needs to wait for others to take some action before they can proceed. For example, in an ultimatum game, player 2 cannot accept or reject before they have seen player 1’s offer.
If you have a
WaitPage in your sequence of pages,
then oTree waits until all players in the group have
arrived at that point in the sequence, and then all players are allowed
If your subsession has multiple groups playing simultaneously, and you
would like a wait page that waits for all groups (i.e. all players in
the subsession), you can set the attribute
wait_for_all_groups = True on the wait page.
For more information on groups, see Groups.
Wait pages can define the following methods:
after_all_players_arrive lets you run some calculations
once all players have arrived at the wait
page. This is a good place to set the players’ payoffs
or determine the winner.
You should first define a method on your Group that does the desired calculations.
Let’s say you called it
You can trigger this method by doing:
after_all_players_arrive = 'set_payoffs'
If you set
wait_for_all_groups = True,
then you should set
after_all_players_arrive to the name of to a method on your Subsession model.
In oTree 2.3 and earlier,
after_all_players_arrive was a method:
def after_all_players_arrive(self): self.group.set_payoffs()
This format will continue to be supported, for compatibility.
Works the same way as with regular pages.
If some players in the group skip the wait page,
after_all_players_arrive() may not be run.
If you set
group_by_arrival_time = True on a WaitPage,
players will be grouped in the order they arrive at that wait page:
class MyWaitPage(WaitPage): group_by_arrival_time = True
For example, if
players_per_group = 2, the first 2 players to arrive
at the wait page will be grouped together, then the next 2 players, and so on.
This is useful in sessions where some participants might drop out (e.g. online experiments, or experiments with consent pages that let the participant quit early), or sessions where some participants take much longer than others.
A typical way to use
group_by_arrival_time is to put it after an app
that filters out participants. For example, if your session has a consent page
that gives participants the chance to opt out of the study, you can make a “consent” app
that just contains the consent pages, and
then have an
This means that if someone opts out in
they will be excluded from the grouping in
If a game has multiple rounds, you may want to only group by arrival time in round 1:
class MyWaitPage(WaitPage): group_by_arrival_time = True def is_displayed(self): return self.round_number == 1
If you do this, then subsequent rounds will keep the same group structure as
round 1. Otherwise, players will be re-grouped by their arrival time
in each round.
group_by_arrival_time copies the group structure to future rounds.)
id_in_groupis not necessarily assigned in the order players arrived at the page.
group_by_arrival_timecan only be used if the wait page is the first page in
If you use
is_displayedon a page with
group_by_arrival_time, it should only be based on the round number. Don’t use
is_displayedto show the page to some players but not others.
group_by_arrival_time = True, then in
creating_session, all players will initially be in the same group. Groups are only created “on the fly” as players arrive at the wait page.
If you need further control on arranging players into groups, use group_by_arrival_time_method().
Before November 2019, this was a method called
and it was on the Page, not the Subsession.
The old format is still compatible.
group_by_arrival_time_method will be added to oTree Studio when it comes out of beta.
If you’re using
group_by_arrival_time and want more control over
which players are assigned together, you can use
Let’s say that in addition to grouping by arrival time, you need each group group to consist of 1 man and 1 woman (or 2 “A” players and 2 “B” players, etc).
If you define a method called
group_by_arrival_time_method on your Subsession,
it will get called whenever a new player reaches the wait page.
The method’s argument is the list of players who are waiting to be grouped
(minus those who have disconnected or closed the page).
If you select some of these players and return them as a list,
those players will be assigned to a group, and move forward.
If you don’t return anything, then no grouping occurs.
Here’s an example where each group has 2 A players, 2 B players.
class Subsession(BaseSubsession): def group_by_arrival_time_method(self, waiting_players): print('in group_by_arrival_time_method') a_players = [p for p in waiting_players if p.participant.vars['type'] == 'A'] b_players = [p for p in waiting_players if p.participant.vars['type'] == 'B'] if len(a_players) >= 2 and len(b_players) >= 2: print('about to create a group') return [a_players, a_players, b_players, b_players] print('not enough players to create a group')
Preventing players from getting stuck on wait pages¶
A common problem especially with online experiments is players getting stuck waiting for another player in their group who dropped out or is too slow.
Here are some things you can do to reduce this problem:
As described above, you can use
group_by_arrival_time so that only
players who are actively playing around the same time get grouped together.
group_by_arrival_time works well if used after a “lock-in” task.
In other words, before your multiplayer game, you can have a
single-player effort task. The idea is that a
participant takes the effort to complete this initial task, they are
less likely to drop out after that point.