Maps controller inputs (Triggers for W/S, Stick for WASD) to keyboard keys for Polytrack on iPad.
// ==UserScript==
// @name Polytrack Controller Mapper
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Maps controller inputs (Triggers for W/S, Stick for WASD) to keyboard keys for Polytrack on iPad.
// @author You
// @match https://*kodub.com/apps/polytrack/*
// @match https://*.polytrack.com/*
// @match https://eelekweb.github.io/polytrack/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=polytrack.com
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
console.log("Polytrack Controller Loaded");
// Key state tracker to prevent spamming the game with events
const keys = {
'w': false,
'a': false,
's': false,
'd': false
};
// Helper to send KeyDown
function triggerDown(key) {
if (keys[key]) return; // Already pressed
keys[key] = true;
let event = new KeyboardEvent('keydown', {
key: key,
code: 'Key' + key.toUpperCase(),
keyCode: key.toUpperCase().charCodeAt(0),
which: key.toUpperCase().charCodeAt(0),
bubbles: true,
cancelable: true,
view: window
});
window.dispatchEvent(event);
document.dispatchEvent(event);
// Sometimes games listen to the canvas specifically
let canvas = document.querySelector('canvas');
if (canvas) canvas.dispatchEvent(event);
}
// Helper to send KeyUp
function triggerUp(key) {
if (!keys[key]) return; // Already released
keys[key] = false;
let event = new KeyboardEvent('keyup', {
key: key,
code: 'Key' + key.toUpperCase(),
keyCode: key.toUpperCase().charCodeAt(0),
which: key.toUpperCase().charCodeAt(0),
bubbles: true,
cancelable: true,
view: window
});
window.dispatchEvent(event);
document.dispatchEvent(event);
let canvas = document.querySelector('canvas');
if (canvas) canvas.dispatchEvent(event);
}
// The Main Loop checks the controller 60 times a second
function gameLoop() {
const gamepads = navigator.getGamepads ? navigator.getGamepads() : [];
if (!gamepads) {
requestAnimationFrame(gameLoop);
return;
}
// We grab the first controller (Index 0)
const gp = gamepads[0];
if (gp) {
// --- INPUT MAPPING ---
// 1. Get Values
// Left Stick: usually axis 0 (left/right) and 1 (up/down)
let stickX = gp.axes[0];
let stickY = gp.axes[1];
// Triggers: typically buttons 6 (LT) and 7 (RT) on iOS/Xbox/PS
// If these don't work, try changing 6/7 to 4/5.
let lt = gp.buttons[6] ? gp.buttons[6].pressed : false;
let rt = gp.buttons[7] ? gp.buttons[7].pressed : false;
// Deadzone (ignore small stick drifts)
let dz = 0.2;
// 2. Logic: "RT for W" OR "Stick Up for W"
if (rt || stickY < -dz) {
triggerDown('w');
} else {
triggerUp('w');
}
// 3. Logic: "LT for S" OR "Stick Down for S"
if (lt || stickY > dz) {
triggerDown('s');
} else {
triggerUp('s');
}
// 4. Logic: Stick Left for A
if (stickX < -dz) {
triggerDown('a');
} else {
triggerUp('a');
}
// 5. Logic: Stick Right for D
if (stickX > dz) {
triggerDown('d');
} else {
triggerUp('d');
}
}
// Keep the loop running
requestAnimationFrame(gameLoop);
}
// Start the loop
gameLoop();
})();