Skip to main content

Motoko playground

Beginner
IDE
Tutorial

Overview

Motoko playground is a development environment that can be used to build and deploy Motoko canisters directly in the web browser using the Motoko playground canister. Motoko playground can also be run locally, or it can be utilized through the dfx deploy --playground command via the CLI. Canisters deployed to the playground use resources that are borrowed from a canister pool. All canisters deployed to the playground will time out after 20 minutes, and new canisters will need to be borrowed for further deployments past the timer.

Motoko playground canisters provide the ability to quickly test canisters and their workflows without needing to configure additional parameters, such as having cycles.

The Motoko playground is comprised of:

  • The playground's frontend asset canister, which contains:
    • A Motoko compiler produced by js-of-ocaml.
    • A Monaco editor that includes support for Motoko syntax validation.
    • A Vessel package manager that loads libraries from the vessel package set.
    • A deploy module which integrates the canister's actor class, Candid UI, and upgrade process.
  • The playground's backend canister that controls all canisters deployed by users on the playground.
  • A wasm-utils canister that performs Wasm analysis and modification to limit certain types of calls.

Each canister has an initial amount of 0.5T cycles and can be used for 20 minutes once the canister has been deployed. To avoid wasting cycles, canisters cannot transfer cycles, and the cycles transfer instructions have been removed.

To ensure resources are used fairly by users, proof of work when a user makes a request for a canister ID is required.

Since Motoko playground is designed for short, small-scale testing, there are limitations and restrictions imposed on canisters deployed to the playground. These limitations are:

  • Cycle transfer instructions are silently ignored by the Motoko playground.
  • Canisters can use at most 1GiB of memory.
  • Canisters can call the management canister to manage itself without being the controller.
  • Deployed canisters expire after 20 minutes. Upgrades to canisters reset this timer. When the timer runs out, the canister(s) will be uninstalled.
  • Wasm files can't be gzipped.
  • Wasm files will be analyzed to remove any potentially expensive/malicious operations.

dfx deploy --playground

Through dfx, canisters can be deployed directly from the CLI to the Motoko playground using the --playground flag. When this flag is used, the canisters in the dfx project will be deployed to the mainnet using borrowed resources from the Motoko playground canister pool, and a canister URL will be returned to the developer. This enables developers to test canister functionality or workflow without needing to set up a obtain cycles or configure other parameters that are required for deployment to the mainnet.

Prerequisites

Before you start, verify the following:

  • You have an internet connection and access to a shell terminal on your local macOS or Linux computer.

  • You have a command line interface (CLI) window open. This window is also referred to as the 'terminal' window.

  • You have downloaded and installed the IC SDK package as described in the installing the IC SDK page.

dfx deploy --playground is available in dfx versions v0.15.1 and above.

To deploy a canister to the Motoko playground, first create a new dfx project with the command:

dfx new hello_world

This command will create a new default dfx project, which contains a simple 'Hello, world!' sample.

Then, to deploy this canister to the playground, run the command:

dfx deploy --playground

Once deployed, the canister can be interacted with using a command such as:

dfx canister --network playground call hello_world_backend greet '("everyone")'

This command calls the hello_world_backend canister that has been deployed to the playground using the --network flag, since the playground is classified as a deployment network.

Any commands that intend to target a canister deployed to the playground must use the --playground or --network playground flag in order to target the borrowed canister(s).

Creating a custom playground with separate pool creation

As an alternative to a staging environment or the Motoko mainnet playground, the Motoko playground allows for custom, private playgrounds to be deployed. Using a custom playground allows for extensive customization, such as enabling access control by restricting the playground's usage to only allow certain principals, configuring more generous canister timeouts and the amount of available cycles, and allowing some (or all) of the function calls that the mainnet Motoko playground does not allow, such as sending cycles to other canisters.

Using a custom playground can help simplify development for teams, since the whole team can use a custom playground without needing to manage individual cycle balances.

  • Step 1: Clone the Motoko playground repo with the command:

git clone https://github.com/dfinity/motoko-playground
  • Step 2: To create a separate pool, first use the current Motoko playground pool and wasm-utils canisters as the starting point.

These can be found here.

  • Step 3: Then, edit the pool/Main.mo file to change your custom playground settings, such as:

  • Add access control here as desired, such as creating an whitelist of principals that are permitted to use the custom playground.

  • Configure the canister's time to live here.

  • Change the Wasm transformation to fit your desired configuration here. In some cases, this may just be wasm = args.wasm_module, since if there is an allowlist in place, the principals allowed to install canisters can be trusted, such as:

let wasm = args.wasm_module;
  • Step 4: Then deploy the pool canister, and if necessary, deploy the wasm-utils canister:

dfx deploy pool
dfx deploy wasm-utils
  • Step 5: Lastly, define the local playground network in your project's dfx.json file. In this definition, you will need to set the playground canister's ID (the pool canister ID) and define the amount of seconds before a canister is returned to the pool, as shown below:

"<network name>": {
"playground": {
"playground_canister": "<canister pool id>",
"timeout_seconds": <amount of seconds after which a canister is returned to the pool>
},
"providers": [
"https://icp0.io"
]
}

If the value <network name> is set as playground, then the command dfx deploy --playground will deploy to your custom playground. Otherwise, the command has to use --network <network name>.

This network definition can also go into networks.json file so it applies to every project, not just the current project, since dfx.json files are project-specific. To see where the networks.json file is located, use the command dfx info networks-json-path.

Motoko playground in the web browser

Motoko playground is also available through the Motoko playground frontend canister, which can be accessed at the following public URL:

https://m7sm4-2iaaa-aaaab-qabra-cai.ic0.app/

In this portion of the guide, you'll explore the frontend canister's UI functionality and how to use it.

When this URL is opened in a web browser, you will see the following welcome screen:

Motoko playground welcome

From this welcome menu, you can choose an example project from the 'Example Projects' tab, which will automatically install the required files and packages for the example, or you can import another project from a Github repo.

Examples

Import from Github

For this example, you'll select the 'Hello, world' example from the 'Example Projects' tab.

Once opened, you will see the following screen. Take note of the project's files in the left side bar menu under 'FILES', and the installed packages under the 'PACKAGES' menu.

To deploy the project, select the 'Deploy' button from the upper right corner of the screen.

Deploy

After selecting 'Deploy', you will be prompted to give your canister a name, then enable additional options such as profiling and the canister's garbage collection strategy. You'll also be reminded of the restrictions imposed by Motoko playground, such as a time limit of 20 minutes.

Canister options

When ready, select 'Install'. Once the canister has been successfully deployed, the log at the bottom of the window will display the canister's ID.

Canister output

In addition to this log output, there will also be a new UI that opens on the right side of the window. This displays the canister's Candid UI. The Candid UI can be used to interact with canister's different defined methods, such as submitting queries to the methods.

Candid UI 1

For this 'Hello, world' canister, there is one method within the canister's code that can be used. This method is a simple 'say' method, which returns any text inputted by the user. Other canisters may have several different methods; it will vary based on the canister's code.

Candid UI 2

To test this method, enter the text 'Welcome to Motoko playground!' in the input box, then select the 'QUERY' button.

Candid UI 3

In the output log, you will see that the canister returns this string of text to you.

Candid UI 4

Next, let's take a closer look at the left side bar menu in the window. This menu has three categories: files, packages, and canisters.

Left menu

To add new files to the Motoko playground workspace, select the + button next to the 'FILES' option.

Add files

Then give the new file a file name.

Create file name

To add new packages to the Motoko playground workspace, select the + button next to the 'PACKAGES' option.

Add packages

From this menu, you can select from the list of pre-imported Vessel packages, or you can input your own from Github.

Packages

Lastly, the 'CANISTERS' portion of the menu showcases your currently running canister(s) and shows the time remaining for the canister's deployment. When this timer runs out, the canister is automatically uninstalled.

To add another canister, select the + button next to the 'CANISTERS' option.

Add canisters

From the 'Add a canister' menu, you can import a canister using the canister's ID, as shown below.

Import by ID

Alternatively, you can deploy a canister using the canister's Wasm and .did files.

Import by wasm

Lastly, let's take a look at the two buttons in the top right corner of the Motoko playground UI: the 'Save and & Share' button and the 'Open Example' button.

The 'Save and Share' button can be used to save your canister's code and share it using a live canister URL. Select this button to demonstrate this workflow.

Save and share

In the log, your canister's URL will be returned for you to share with others:

Canister URL

Then, the 'Open Example' button can be used to open another pre-configured example, or import your own from Github.

Open example

This menu will have the same examples and options that the initial welcome menu contained.

Examples

Creating editor integrations

The Motoko playground has limited support for cross-origin communication. For developers building custom smart contract editors or similar applications, the following code snippet can open the project in Motoko playground:

const PLAYGROUND_ORIGIN = 'https://play.motoko.org'
const APP_ID = 'MyEditor'
// Workplace files for a project
const userFiles = {
'Main.mo': 'actor { public func hello() : async Text { "Hello World" } }'
}
// GitHub package dependencies for a project
const userPackages = [{
name: 'quicksort',
repo: 'https://github.com/dfinity/examples.git',
version: 'master',
dir: 'motoko/quicksort/src'
}]
// Open the Motoko Playground in a new window
const playground = window.open(`${PLAYGROUND_ORIGIN}?post=${APP_ID}`, 'playground')
// Call repeatedly until loaded (interval ID used for acknowledgement)
const ack = setInterval(() => {
const request = {
type: 'workplace',
acknowledge: ack,
packages: userPackages,
actions: [{
type: 'loadProject',
payload: {
files: userFiles
}
}],
deploy: true
}
// Concatenate APP_ID and request JSON
const data = APP_ID + JSON.stringify(request)
console.log('Request data:', data)
playground.postMessage(data, PLAYGROUND_ORIGIN)
}, 1000)
// Listen for acknowledgement
const responseListener = ({source, origin, data}) => {
if(
typeof data === 'string' &&
data.startsWith(APP_ID) &&
source === playground &&
origin === PLAYGROUND_ORIGIN
) {
console.log('Response data:', data)
// Parse JSON part of message (prefixed by APP_ID)
const response = JSON.parse(data.substring(APP_ID.length))
if(response.acknowledge === ack) {
clearInterval(ack)
window.removeEventListener('message', responseListener)
}
}
}
window.addEventListener('message', responseListener)

This example works for localhost out of the box. To use this feature in production, please submit a PR to the Motoko playground repository that adds your application's public URL to the file src/integrations/allowedOrigins.js.