triplex icon indicating copy to clipboard operation
triplex copied to clipboard

Fine grained control of the frame loop using play controls

Open itsdouges opened this issue 1 year ago • 1 comments

After completing https://github.com/trytriplex/triplex/issues/17 we implemented a bare bones implementation that runs the scene, plays through the default camera, turns on raycasting and userland controls. What wasn't implemented was proper support for controlling the frame loop, meaning when in edit mode the frame loop is stopped and so on.


When fixing the scroll controls issue and discussing options with @krispya he proposed exploring an event hijacking solution where all user land events are suppressed when the editor is in edit / pause modes.

We'd need to explore a method of whitelisting events we want to allow but this sounds promising.

This should block all user land events from:

  • add event listener
  • request animation frame
  • set timeout
  • set interval

itsdouges avatar Sep 05 '24 21:09 itsdouges

Here is the Claude code to get started with:

// Store original methods
const originalAddEventListener = EventTarget.prototype.addEventListener;
const originalRemoveEventListener = EventTarget.prototype.removeEventListener;

// Create a WeakMap to store listeners
const listenerMap = new WeakMap();

// Override addEventListener
EventTarget.prototype.addEventListener = function(type, listener, options) {
    if (!listenerMap.has(this)) {
        listenerMap.set(this, {});
    }
    
    if (!listenerMap.get(this)[type]) {
        listenerMap.get(this)[type] = [];
    }
    
    listenerMap.get(this)[type].push({ listener, options });
    
    return originalAddEventListener.call(this, type, listener, options);
};

// Override removeEventListener
EventTarget.prototype.removeEventListener = function(type, listener, options) {
    const listeners = listenerMap.get(this);
    if (listeners && listeners[type]) {
        const index = listeners[type].findIndex(l => l.listener === listener);
        if (index !== -1) {
            listeners[type].splice(index, 1);
        }
    }
    
    return originalRemoveEventListener.call(this, type, listener, options);
};

// Function to remove all listeners
function removeAllListeners(element, type) {
    const listeners = listenerMap.get(element);
    if (listeners && listeners[type]) {
        listeners[type].forEach(({ listener, options }) => {
            originalRemoveEventListener.call(element, type, listener, options);
        });
        listeners[type] = [];
    }
}

// Function to restore all listeners
function restoreAllListeners(element, type) {
    const listeners = listenerMap.get(element);
    if (listeners && listeners[type]) {
        listeners[type].forEach(({ listener, options }) => {
            originalAddEventListener.call(element, type, listener, options);
        });
    }
}

krispya avatar Sep 07 '24 21:09 krispya

I'm not sure this is something people actually want. Would love any comments from the community on this.

itsdouges avatar Jul 24 '25 00:07 itsdouges

For now I'm dropping this as low priority — I'm not sure if there are legitimate needs to implement this that are already covered by alternate implementations.

If you think strongly about needing this please raise another issue with your use case and let's talk!

itsdouges avatar Jul 30 '25 10:07 itsdouges