Skip to content

6 Scripting (beta)

Miika Kuisma edited this page Dec 19, 2023 · 4 revisions

Introduction

Sumo custom allows you to run commands and more by writing JavaScript right in the application's scripting window.

Scripts are stored inside work data everytime you hit Run button. As the script is inside work data it will be included in the backup and cloud save.

When user who're not using scripting open a work that contains script, they will see a prompt asking if they would like to Run the script.

Getting Started

In order to enable scripting you need to start Sumo custom with URL param &script=true. When scripting is available you will see code button on bottom left corner of the screen.

Tatami variables

While you script is running you have read-only access to some internal variables of our Tatami engine. Here is a sample of this tatami object. PS. This object will have more items in near future.

tatami = {
    width: 1024, // width of the canvas
    height: 1024, // height of the canvas
}

Commands

To use commands in your script simply use command method and pass command string as parameter.

For example if we like to set brush size to 60.

command('brush-size:60')

To use it with variable

let size = 60
command('brush-size:'+size)

For complete list of available commands. See Commands chapter for more info.

Events

There are certain built-in events you can use

onRunStart

This event gets fired when we press Run button and script gets executed. In the following example we would start animation loop when script starts.

onRunStart = () => {
    console.log('run button pressed')
    requestAnimationFrame(draw)
}

onRunStop

This event gets fired when script execution ends.

onRunStop = () => {
    console.log('stop button pressed')
    // Here you might want to stop animations, loops etc
}

onFrame

This event gets fired during script execution at every frame. Yes, this is the place to do scripted those animations.

let hue = -1.0

onFrame = () => {
    if (hue >= 1.0) {
        hue = -1.0
    } else {
        hue = hue + 0.01
    }
    command('adjustment-hue:' + hue.toFixed(2))
}

onKey

This event gets fired during script execution when keyboard keys are pressed. Method receives code parameter from keyboard handler, which is equivalent to e.code in standard JavaScript keyboard events.

let size = 100

onKey = (code) => {
    if (code === 'ArrowUp') {
        size = size + 10
    }
    if (code === 'ArrowDown') {
        size = size - 10
        if (size <= 0) size = 10
    }
    command('brush-size:' + size)
}

onMessage

This event gets fired when script receives message from Sumo custom. Basically every command and UI interaction sends a message.

If you want to see the messages in action, put this code into the script window, Run the script and then try using the app normally. You should see messages appearing in the console view of the scripting window.

onMessage = (msg) => console.log(msg)

Example 1 - Drawing Programmatically

Here is full example of how you could draw by scripting brush strokes. For this we need to use pointer commands. It's important to know that each brush stroke requires all three events (start, move, end) to work properly.

// Set brush color to blue
command('brush-color:#0000cc')
command('brush-size:60')
// Pointer modes: 0 = draw, 1 = pan
const mode = 0
// Read canvas width and height from Tatami object
const width = tatami.width
const height = tatami.height
// Reset frame counter
let i = 0

// Our main drawing function
const draw = () => {
  let x = Math.sin(-i)
  let y = Math.cos(i)
  // Try changing some values here and see what happens :)
  const pointerX = width/2 + x * 8 * i
  const pointerY = height/2 + y * 8 * i
  command(`pointer-move:${mode}, ${pointerX}, ${pointerY}`)
  i += 0.2 // modify this to change speed
}

onRunStart = () => {
    console.log('run button pressed')
    // Start stroke at center
    const centerX = width/2
    const centerY = height/2
    command(`pointer-start:${mode}, ${centerX}, ${centerY}`)
    // Start interval timer to fire every 20ms
    window.timer = setInterval(() => {
        draw()
    }, 20)
}

onRunStop = () => {
    console.log('stop button pressed')
    // End stroke back to center
    const centerX = width/2
    const centerY = height/2
    command(`pointer-end:${mode}, ${centerX}, ${centerY}`)
    // Terminate our interval timer
    clearInterval(window.timer)
}

To see this example work live, visit https://sumo.app/works/code-draw

Example 2 - Interacting with Weather API

In this example we're going to show current weather. We need to prepare a work which has one layer for each weather condition (such as clear, clouds, rain etc). Then as we fetch weather data from openweathermap.org we can then set visibility of the layers to display the current condition. In addition to this we will add new text element with current temperature value.

const apiKey = 'YOUR_API_KEY';
const city = 'Helsinki';
const countryCode = 'FI';

const apiUrl = `https://api.openweathermap.org/data/2.5/weather?q=${city},${countryCode}&appid=${apiKey}&units=metric`;

fetch(apiUrl)
  .then(response => response.json())
  .then(data => {
    // Output the response with all weather data that we received
    console.log(data)
    // Hide all weather condition layers
    command('layer-hide:0') // Thunderstorm
    command('layer-hide:1') // Snow
    command('layer-hide:2') // Drizzle
    command('layer-hide:3') // Clouds
    command('layer-hide:4') // Rain
    command('layer-hide:5') // Clear
    // Set color to white, so that the text we will add next will be white
    command('brush-color:#ffffff')
    // Create new text element with temperature value
    command(`text-add:${parseInt(data.main.temp)}:128:512:768:1024:256`)
    // Read name of the weather condition
    const weatherCondition = data.weather[0].main
    // Here is link to all the different conditions there are
    // https://openweathermap.org/weather-conditions#Weather-Condition-Codes-2

    // Then we set the right layer visible (0 = the bottom layer)
    switch (weatherCondition) {
        case 'Clear':
            command('layer-show:5')
            break
        case 'Rain':
            command('layer-show:4')
            break
        case 'Clouds':
            command('layer-show:3')
            break
        case 'Drizzle':
            command('layer-show:2')
            break
        case 'Snow':
            command('layer-show:1')
            break
        case 'Thunderstorm':
            command('layer-show:0')
            break
        default:
            break
    }
  })
  .catch(error => console.error('Error fetching weather data:', error))

To see this example work live, visit https://sumo.app/works/weatherapi

Clone this wiki locally