Threads
How parallel script execution works in TapForge
spawnThread starts a function on a separate thread. The calling script continues immediately — spawnThread doesn't wait for the function to finish. Each spawned thread runs in its own independent JavaScript environment.
At most 64 spawned threads may be alive at once; further spawns are silently dropped. region.highlight() shares that budget — its deferred clear is a spawned thread, so a loop that highlights on every pass is spending slots even though it never calls spawnThread.
What threads share — and what they don't
Each thread gets its own isolated JS context. Regular variables from the main script (let, var, const) are not visible in a spawned thread. Top-level function declarations are the exception — they are text-copied into the thread's environment before it starts, so helper functions you define at the top of your script work inside threads.
The only thread-safe shared state is setGlobalVar and getGlobalVar. These are backed by a synchronized map shared across all threads and the main script. Use them to pass signals and values between threads.
// Top-level helpers are available in threads
function formatCount(n) { return n + " items"; }
spawnThread(function() {
print(formatCount(10)); // works — top-level function was copied in
print(typeof someMainVar); // "undefined" — regular vars are not shared
});
Thread lifecycle and stopping
isStopped() works inside a spawned thread exactly like in the main script — it returns true when the user presses Stop, when any script calls exit(), or when the main script finishes. Always check it in loops so threads don't outlive the macro run.
When the main script finishes, all spawned threads are interrupted and TapForge waits up to ten seconds total — a single budget shared across every thread, not ten seconds each — for them to exit. A thread that calls wait() or checks isStopped() regularly will respond within a fraction of a second. A thread stuck in a tight loop without any check may be abandoned once the budget runs out.
spawnThread(function() {
while (!isStopped()) { // exits cleanly when the macro stops
var count = getGlobalVar("item_count") || 0;
if (count > 50) {
setGlobalVar("bag_full", true);
}
wait(500);
}
});
Errors in spawned threads
If a spawned thread throws an error, it is logged but does not stop the macro or notify the main script — the main script keeps running. If a thread calls exit(), it stops the entire runner, including the main script and all other threads.
Performance: what actually runs in parallel
Android allows only one active gesture at a time, and the newest one wins: dispatching a gesture cancels whichever is already in flight. The cancelled call returns false. If multiple threads each call longPress simultaneously, they cancel each other in a rapid loop (visible flickering). Use multiLongPress when you need to hold several touch points at once.
However, visual processing is heavily parallelized. Functions like findObject, findColor, readColor, ocr, and the visual search phase of findAndTap immediately offload their heavy processing (like OpenCV template matching) to a background thread pool. If you spawn multiple threads that all call visual functions simultaneously, they will process their screenshots at the exact same time across different CPU cores.
The practical use case for threads is parallel logic, not parallel input: a background thread monitors the screen for an event (a popup, a resource icon appearing, a danger indicator) while the main script runs its normal loop, and signals the main script via setGlobalVar when something is detected.
Example: background popup monitor
// Background thread watches for an error popup
spawnThread(function() {
while (!isStopped()) {
if (findObject("error_popup")) {
setGlobalVar("popup", true);
}
wait(800);
}
});
// Main loop: farm normally, handle popup when signalled
while (!isStopped()) {
if (getGlobalVar("popup")) {
findAndTap("dismiss_button");
setGlobalVar("popup", false);
wait(500);
continue;
}
findAndTap("collect_resource");
wait(2000);
}