Screen Reading & Scaling

OCR, color detection, image matching, and coordinate scaling

ocr(regionName)

Reads text from a named screen region using OCR.

ParameterTypeDescription
regionNamestringName of the region object defined in the visual editor

Returns string — recognized text, or "" if nothing was read

var score = ocr("score_region");
print("Score: " + score);

ocrAt(x, y, w, h)

Reads text from a screen region defined by its top-left corner and fixed size using OCR. All coordinates are in current device pixels.

ParameterTypeDescription
x, ynumberTop-left coordinates of the region in pixels
w, hnumberWidth and height of the region in pixels

Returns string — recognized text, or "" if nothing was read

// Fixed position (top-left x=440, y=100, width=200, height=40)
var score = ocrAt(440, 100, 200, 40);

// Dynamic — read text next to a found element
// Match.x/y is the top-left corner; Match.w/h is the bounding box size
var pos = findText("Score:");
if (pos !== null) {
  var value = ocrAt(pos.x + pos.w + 8, pos.y, 150, pos.h);
}

readColor(x, y)

Reads the color of a single pixel at the given coordinates in current device pixels.

ParameterTypeDescription
x, ynumberPixel coordinates

Returns string — hex color string in the format "#RRGGBB"

var color = readColor(540, 100);
if (color === "#FF0000") {
  print("Red pixel detected");
}

findColor(colorHex, options?)

Searches for the first pixel matching the given color within a per-channel tolerance. Scans top-to-bottom, left-to-right and returns the first hit.

ParameterTypeDefaultDescription
colorHexstringTarget color in "#RRGGBB" format
options.tolerancenumber15Maximum per-channel deviation 0–255. 0 = exact match; 15 allows ±15 on each R, G, B channel independently.
options.regionRegionConstrains the scan to a rectangular region. If omitted, the entire screen is searched.

Returns Match | null — a 1×1 Match at the found pixel. Call .tap() directly or read .cx/.cy for the coordinates. Returns null if not found.

// Search the whole screen and tap the result
var hit = findColor("#FF5C3D", { tolerance: 20 });
if (hit !== null) {
  hit.tap();
}

// Constrain to a fixed region
var hit = findColor("#FF5C3D", { tolerance: 20, region: new Region(100, 400, 300, 200) });

// Use a scene's stored search region (auto-scaled to current screen)
var hit = findColor("#FF5C3D", { region: Region.fromScene("battle_scene") });

// Dynamic region — search near a previously found object
var enemy = getPosition("enemy_icon");
if (enemy !== null) {
  var nearby = findColor("#FF0000", { region: new Region(enemy.cx - 60, enemy.cy - 60, 120, 120) });
}

// Exact match, no tolerance
var exact = findColor("#00FF00", { tolerance: 0 });

findColors(colorHex, options?)

Searches for all pixels matching the given color within a per-channel tolerance. Scans top-to-bottom, left-to-right and returns every hit as an array.

ParameterTypeDefaultDescription
colorHexstringTarget color in "#RRGGBB" format
options.tolerancenumber15Maximum per-channel deviation 0–255. 0 = exact match; 15 allows ±15 on each R, G, B channel independently.
options.regionRegionConstrains the scan to a rectangular region. If omitted, the entire screen is searched.
options.maxResultsnumber0Maximum number of results to return. 0 means no limit. Use this to avoid scanning unnecessarily when you only need a few matches.

Returns Match[] — array of 1×1 Match objects at each found pixel. Empty array if none found.

// Find all red pixels on screen and tap each
var hits = findColors("#FF0000", { tolerance: 20 });
print("Found " + hits.length + " matches");
hits.forEach(function(m) {
  m.tap();
  wait(100);
});

// Limit to 5 results within a region
var hits = findColors("#FFD700", {
  tolerance: 15,
  region: new Region(0, 400, 1080, 800),
  maxResults: 5
});

// Check if any match exists
if (findColors("#FF0000", { tolerance: 10, maxResults: 1 }).length > 0) {
  print("Red detected");
}
Tip: When you only need to detect presence, pass maxResults: 1 to stop scanning after the first match.

findAllText(query, options?)

Searches the screen for all occurrences of the given text using OCR. Matching is always case-insensitive.

ParameterTypeDefaultDescription
querystringText to search for
options.regionRegionConstrains the OCR scan to a rectangular region.
options.wholeWordbooleanfalseWhen true, only matches the query when it appears as a standalone word — not as part of a longer word. Returns the precise bounding box of the matched word rather than the whole line.

Returns Match[] — array of matches. cx/cy is the text center; x/y/w/h is the bounding box. Empty array if none found.

findAllText("Delete").forEach(function(m) {
  m.tap();
  wait(300);
});

// Only search within the bottom half of the screen
findAllText("Collect", { region: new Region(0, 960, 1080, 960) }).forEach(function(m) {
  m.tap();
});

// Whole-word match: finds "Start" but not "Restart" or "Starting"
findAllText("Start", { wholeWord: true }).forEach(function(m) {
  m.tap();
});

findText(query, options?)

Searches the screen for the first occurrence of the given text using OCR. Convenience wrapper for findAllText(query, options)[0]. Matching is always case-insensitive.

ParameterTypeDefaultDescription
querystringText to search for
options.regionRegionConstrains the search to a specific area
options.wholeWordbooleanfalseWhen true, only matches the query when it appears as a standalone word — not as part of a longer word. Returns the precise bounding box of the matched word rather than the whole line.

Returns Match | null — the first match, or null if none found. cx/cy is the text center; x/y is the top-left corner of the bounding box.

var m = findText("Score:");
if (m !== null) {
  m.tap();
}

// Read the value to the right of a label
// m.x/y is the top-left corner; m.w/h is the bounding box size
var label = findText("HP:");
if (label !== null) {
  var value = ocrAt(label.x + label.w + 8, label.y, 120, label.h);
}

// Whole-word match: finds "test" but not "testa" or "atest"
var m = findText("test", { wholeWord: true });
if (m !== null) {
  m.tap();
}

getPosition(objectName, options?)

Finds a UI object by image matching and returns its screen position without tapping it. If the object has a search region set in the editor, it is applied automatically.

ParameterTypeDefaultDescription
objectNamestringName of the UI object
options.thresholdnumber0.70Minimum match confidence 0.0–1.0
options.regionRegionOverrides the object's search region.
options.matchTimeoutnumberMaximum milliseconds to spend on the pyramid search. The search stops early after the allotted time; any scales already completed are still used. Omit for no limit.

Returns Match | nullcx/cy is the object center, x/y/w/h is the bounding box, score is the confidence. Returns null if not found.

var pos = getPosition("enemy_icon");
if (pos !== null) {
  pos.tap(); // Tap the matched object
}

getPosition("enemy_icon", { threshold: 0.85 });
getPosition("enemy_icon", { region: new Region(0, 400, 1080, 800) });
getPosition("enemy_icon", { matchTimeout: 200 }); // stop pyramid search after 200 ms

// Use a scene's stored search region:
getPosition("enemy_icon", { region: Region.fromScene("battle_scene") });

// Use a Region to search for a color inside the matched object
var icon = getPosition("item_icon");
if (icon !== null) {
  var hit = icon.findColor("#FFD700");
}

waitForObject(objectName, options?)

Blocks until the given object appears on screen or the timeout elapses.

ParameterTypeDefaultDescription
objectNamestringName of the UI object
options.timeoutnumber5000Max wait time in milliseconds
options.intervalnumber500Polling interval in milliseconds
options.thresholdnumber0.70Minimum match confidence 0.0–1.0
options.regionRegionConstrains the search to a rectangular area
options.matchTimeoutnumberMaximum milliseconds to spend on the pyramid search per poll iteration. Omit for no limit.

Returns Match | null — the found object, or null if the timeout elapsed

waitForObject("spinner");
waitForObject("spinner", { timeout: 10000 });
waitForObject("spinner", { threshold: 0.8 });
waitForObject("spinner", { timeout: 10000, interval: 250, threshold: 0.7 });
waitForObject("spinner", { region: new Region(200, 300, 680, 400) });

// Use the returned Match immediately
var pos = waitForObject("reward_box", { timeout: 8000 });
if (pos !== null) {
  pos.tap();
}

waitForText(query, options?)

Blocks until the given text appears anywhere on screen (via OCR) or the timeout elapses. Matching is always case-insensitive.

ParameterTypeDefaultDescription
querystringText to search for
options.timeoutnumber5000Max wait time in milliseconds
options.intervalnumber500Polling interval in milliseconds
options.regionRegionConstrains the OCR scan to a rectangular area
options.wholeWordbooleanfalseWhen true, only matches when the query appears as a standalone word — not as part of a longer word

Returns booleantrue if the text appeared within the timeout

waitForText("Loading…");
waitForText("Loading…", { timeout: 10000 });
waitForText("Loading…", { timeout: 10000, interval: 250 });
waitForText("Ready", { region: new Region(0, 800, 1080, 400) });
waitForText("Start", { wholeWord: true }); // won't match "Restart"

waitUntilGone(objectName, options?)

Blocks until the given object disappears from the screen (template match returns no results) or the timeout elapses.

ParameterTypeDefaultDescription
objectNamestringName of the UI object
options.timeoutnumber5000Max wait time in milliseconds
options.intervalnumber500Polling interval in milliseconds
options.thresholdnumber0.70Minimum match confidence 0.0–1.0
options.regionRegionConstrains the search to a rectangular area
options.matchTimeoutnumberMaximum milliseconds to spend on the pyramid search per poll iteration. Omit for no limit.

Returns booleantrue if the object disappeared within the timeout

findAndTap("confirm_button");
waitUntilGone("confirm_button");
waitUntilGone("confirm_button", { timeout: 8000 });
waitUntilGone("confirm_button", { timeout: 8000, interval: 250, threshold: 0.8 });
waitUntilGone("confirm_button", { region: new Region(200, 300, 680, 400) });

waitUntilTextGone(query, options?)

Blocks until the given text disappears from the screen (OCR returns no results) or the timeout elapses.

ParameterTypeDefaultDescription
querystringText to watch for
options.timeoutnumber5000Max wait time in milliseconds
options.intervalnumber500Polling interval in milliseconds
options.regionRegionConstrains the OCR scan to a rectangular area

Returns booleantrue if the text disappeared within the timeout

findAndTap("close_button");
waitUntilTextGone("Loading…");
waitUntilTextGone("Loading…", { timeout: 8000 });
waitUntilTextGone("Connecting", { region: new Region(0, 0, 1080, 200) });

getPositions(objectName, options?)

Finds all image matches of a UI object on screen by template matching.

ParameterTypeDefaultDescription
objectNamestringName of the UI object
options.thresholdnumber0.70Minimum match confidence 0.0–1.0
options.regionRegionConstrains the search to a rectangular area.
options.matchTimeoutnumberMaximum milliseconds to spend on the pyramid search. The search stops early after the allotted time; any scales already completed are still used. Omit for no limit.

Returns Match[] — array of matches sorted by confidence (highest first). Empty array if none found.

var matches = getPositions("item_icon");
for (var i = 0; i < matches.length; i++) {
  matches[i].tap();
  wait(200);
}

// only high-confidence matches
getPositions("item_icon", { threshold: 0.75 }).forEach(function(m) {
  print("Found at " + m.cx + "," + m.cy + " conf=" + m.score);
  m.tap();
  wait(200);
});

// constrain search to a region
getPositions("item_icon", { region: new Region(0, 600, 1080, 600) });
Note: nextMatch(), tapCurrentMatch(), and ocrCurrentMatch() are used internally by the visual editor's generated code and do not need to be called in handwritten scripts.