Part 2: Trust game

Now let’s create a 2-player Trust game, and learn some more features of oTree.

To start, Player 1 receives 10 points; Player 2 receives nothing. Player 1 can send some or all of his points to Player 2. Before P2 receives these points they will be tripled. Once P2 receives the tripled points he can decide to send some or all of his points to P1.

The completed app is here.

Create the app

Just as in the previous part of the tutorial, create another app, called my_trust.


Go to your app’s Constants.

First we define our app’s constants. The endowment is 10 points and the donation gets tripled.

class Constants(BaseConstants):
    name_in_url = 'my_trust'
    players_per_group = 2
    num_rounds = 1

    endowment = c(10)
    multiplication_factor = 3


Then we add fields to player and group. There are 2 critical data points to record: the “sent” amount from P1, and the “sent back” amount from P2.

Your first instinct may be to define the fields on the Player like this:

# Don't copy paste this
class Player(BasePlayer):

    sent_amount = models.CurrencyField()
    sent_back_amount = models.CurrencyField()

The problem with this model is that sent_amount only applies to P1, and sent_back_amount only applies to P2. It does not make sense that P1 should have a field called sent_back_amount. How can we make our data model more accurate?

We can do it by defining those fields at the Group level. This makes sense because each group has exactly 1 sent_amount and exactly 1 sent_back_amount:

class Group(BaseGroup):

    sent_amount = models.CurrencyField(
        label="How much do you want to send to participant B?"
    sent_back_amount = models.CurrencyField(
        label="How much do you want to send back?"

Define the templates and pages

We need 3 pages:

  • P1’s “Send” page
  • P2’s “Send back” page
  • “Results” page that both users see.

Send page

class Send(Page):

    form_model = 'group'
    form_fields = ['sent_amount']

    def is_displayed(self):
        return self.player.id_in_group == 1

Also, we use is_displayed() to only show this to P1; P2 skips the page. For more info on id_in_group, see Groups.

For the template, set the title block to Trust Game: Your Choice, and the content block to:

You are Participant A. Now you have {{Constants.endowment}}.

{% formfields %}

{% next_button %}


This is the page that P2 sees to send money back. Set the title block to Trust Game: Your Choice, and the content block to:

    You are Participant B. Participant A sent you {{group.sent_amount}}
    and you received {{tripled_amount}}.

{% formfield group.sent_back_amount %}

{% next_button %}

Here is the code from Notes:

  • We use vars_for_template() to pass the variable tripled_amount to the template. You cannot do calculations directly in the HTML code, so this number needs to be calculated in Python code and passed to the template.
  • We define a method sent_back_amount_choices to populate the dropdown menu dynamically. This is the feature called {field_name}_choices, which is explained here: Dynamic form field validation.
class SendBack(Page):

    form_model = 'group'
    form_fields = ['sent_back_amount']

    def is_displayed(self):
        return self.player.id_in_group == 2

    def vars_for_template(self):
        return {
            'tripled_amount': * Constants.multiplication_factor

    def sent_back_amount_choices(self):
        return currency_range(
   * Constants.multiplication_factor,


The results page needs to look slightly different for P1 vs. P2. So, we use the {% if %} statement to condition on the current player’s id_in_group. Set the title block to Results, and the content block to:

{% if player.id_in_group == 1 %}
        You sent Participant B {{ group.sent_amount }}.
        Participant B returned {{ group.sent_back_amount }}.
{% else %}
        Participant A sent you {{ group.sent_amount }}.
        You returned {{ group.sent_back_amount }}.

{% endif %}

Therefore, your total payoff is {{ player.payoff }}.
class Results(Page):

Wait pages and page sequence

This game has 2 wait pages:

  • P2 needs to wait while P1 decides how much to send
  • P1 needs to wait while P2 decides how much to send back

After the second wait page, we should calculate the payoffs. So, we use after_all_players_arrive.

So, we define these pages:

class WaitForP1(WaitPage):

class ResultsWaitPage(WaitPage):

    def after_all_players_arrive(self):
        group =
        p1 = group.get_player_by_id(1)
        p2 = group.get_player_by_id(2)
        p1.payoff = Constants.endowment - group.sent_amount + group.sent_back_amount
        p2.payoff = group.sent_amount * Constants.multiplication_factor - group.sent_back_amount


An equivalent way would be to define the payoff function in like this (note that the group is called self in this context):

class Group(BaseGroup):

    def set_payoffs(self):
        p1 = self.get_player_by_id(1)
        p2 = self.get_player_by_id(2)
        p1.payoff = Constants.endowment - self.sent_amount + self.sent_back_amount
        p2.payoff = self.sent_amount * Constants.multiplication_factor - self.sent_back_amount

Then, we could call it (“trigger it”) in after_all_players_arrive like this:

def after_all_players_arrive(self):

This is actually the technique that’s used more in the sample games. Although it looks a bit more complex, you will see over time that putting your game’s logic in helps with organization.

(The name set_payoffs is arbitrary.)

Then we define the page sequence:

page_sequence = [

Add an entry to your SESSION_CONFIGS

  • name: my_trust
  • display_name: My Trust Game (Simple Version)
  • num_demo_participants: 2
  • app_sequence: [‘my_trust’]

Run the server

Run your server and open your browser to http://localhost:8000 to play the game.