How Lighthouse Minimizes Input Lag in the Port: 4 Engine Techniques Explained

Lighthouse minimizes input lag through early input polling, raw-input recording, angle-lerp interpolation, and configurable input-lag compensation buffers.

The HarbourMasters/Lighthouse engine introduces several low-level techniques to reduce perceived delay between controller actions and on-screen response. These optimizations are critical for the port's networked multiplayer feature, where input lag can make or break the gameplay experience. Below is a detailed breakdown of how the engine achieves responsive input handling, with direct references to the source code implementation.

Early Input Polling: Capturing Controller State First

Early input polling ensures the freshest controller data is available for each game tick. Rather than reading input after game logic or rendering—which would introduce a full frame of delay—the engine polls hardware immediately.

In src/core2/ba/bainput.c, the bainput_update() function performs this polling:

/* src/core2/ba/bainput.c – early input update */
void bainput_update(void) {
    // Poll controller hardware first thing each frame
    controller_poll(&gControllerState);
    // Store raw values for later replay
    gRawInputBuffer[gCurrentFrame] = gControllerState;
}

This function is called at the start of each tick from src/core2/ba/bsmethods.c, guaranteeing that input capture precedes all other frame processing. By eliminating the one-frame "wait" common in engines that poll input later, Lighthouse achieves more responsive controls.

Raw-Input Recording: Preserving Exact Controller State

To support networked multiplayer and demo playback, Lighthouse stores every frame's raw stick axes and button flags in a circular buffer. This raw-input recording captures unprocessed controller data rather than interpreted actions, enabling precise re-simulation during replay.

The recording occurs in src/core2/sprite/render.c, with explicit comments noting its purpose for interpolation:

/* src/core2/sprite/render.c – recording raw inputs for replay */
void render_frame(void) {
    // [port] Record the raw inputs so replay can angle-lerp them against
    record_raw_input(gRawInputBuffer[gCurrentFrame]);
    // …
}

Comments at lines 90 and 231 document this design decision, linking the recording phase directly to the interpolation system used during replay.

Angle-Lerp Interpolation: Smoothing Networked Motion

When replaying recorded inputs—whether for LAN multiplayer or demo playback—Lighthouse applies angle-lerp interpolation to smooth timing differences caused by network latency or frame-rate variations. This technique linearly interpolates between stored input states, producing fluid motion even when replay runs at a different tick rate than original capture.

The interpolation uses angle-aware lerping to handle the circular nature of analog stick values:

/* Simple angle-lerp used during replay */
static float lerp_angle(float a, float b, float t) {
    float diff = fmodf(b - a + 180.0f, 360.0f) - 180.0f;
    return a + diff * t;
}

/* Applying the lerp when replaying inputs */
void replay_input(Frame *prev, Frame *next, float alpha) {
    gReplayedStickX = lerp_angle(prev->stickX, next->stickX, alpha);
    gReplayedStickY = lerp_angle(prev->stickY, next->stickY, alpha);
    gReplayedButtons = (alpha < 0.5f) ? prev->buttons : next->buttons;
}

This interpolation eliminates visual stutter that would otherwise occur from discrete input samples, making networked play feel responsive despite underlying latency.

Input-Lag Compensation Buffers: Absorbing Network Spikes

Lighthouse implements a configurable input buffer to absorb occasional latency spikes without stalling game state. Located in include/variables.h, the INPUT_LAG_BUFFER_FRAMES setting controls buffer depth—typically a few frames.

The buffer flushes only when the replay catches up, maintaining responsive gameplay during transient network issues. Synchronization logic in src/core2/ba/bsmethods.c handles this flushing behavior, ensuring the system recovers smoothly from input delays.

Key Source Files for Input-Lag Minimization

File Role in Input-Lag Reduction
src/core2/ba/bainput.c Core input polling and raw-input buffer population
src/core2/ba/bsmethods.c Orchestrates bainput_update() calls; handles buffer flushing
src/core2/sprite/render.c Records raw inputs with interpolation hints
include/variables.h Defines INPUT_LAG_BUFFER_FRAMES configuration

Summary

Lighthouse minimizes input lag in the port through four integrated techniques:

  • Early input polling captures controller state before any frame processing begins
  • Raw-input recording preserves exact analog and digital inputs for deterministic replay
  • Angle-lerp interpolation smooths input replay across varying frame rates and network conditions
  • Configurable input buffers absorb latency spikes without gameplay stalls

These mechanisms work together to synchronize remote players with minimal visual delay, preserving the responsive feel of the original console experience across networked connections.

Frequently Asked Questions

How does early input polling reduce input lag compared to standard engines?

Standard engines often poll input after game logic or rendering, forcing player actions to wait up to one full frame before affecting the game state. Lighthouse calls bainput_update() at the very start of each tick—before any processing—so controller data is immediately available for the current frame's simulation.

What is angle-lerp interpolation and why does Lighthouse use it?

Angle-lerp interpolation is a specialized linear interpolation that correctly handles the circular wraparound of angle values (0° to 360°). Lighthouse uses it to smoothly transition between recorded analog stick positions during replay, preventing discontinuities that would cause jerky movement when network conditions vary.

Can the input-lag buffer size be adjusted for different network conditions?

Yes. The INPUT_LAG_BUFFER_FRAMES variable in include/variables.h allows developers or advanced users to tune buffer depth. Larger buffers tolerate more latency variance but increase baseline delay; smaller buffers maximize responsiveness but risk stalls on network spikes.

Why does Lighthouse record raw inputs instead of processed game actions?

Raw inputs—uninterpreted stick axes and button flags—enable deterministic replay across different game builds and platforms. Recording processed actions would encode assumptions about control schemes and dead zones that might differ between original capture and replay environments.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →