← Back to Blog

User Overlay: Real-Time In-Game Navigation HUD

When players first launched EVE Frontier, navigation was a manual process: check the star map (static), remember your route (mental note), jump through gates (one by one), repeat. We asked ourselves: what if your route appeared directly in the game as a heads-up display?

That vision became the User Overlay—a DirectX 12 HUD rendered inside EVE Frontier that shows your current route, next waypoint, distance remaining, and estimated time. It's like GPS navigation for space truckers, and it's changed how thousands of players navigate New Eden.

The Vision: Seamless Navigation

Traditional game tools run in separate windows: alt-tab to check the map, memorize the next three jumps, alt-tab back to the game, repeat. This breaks immersion and slows down gameplay. We wanted something better:

Goal: Routes calculated on EF-Map should appear inside the game without any manual copying, alt-tabbing, or separate windows.

This required building native integration with the game client—no small task for a third-party tool. Here's how we made it happen.

Architecture: Three Components in Harmony

The overlay system has three pieces:

1. Helper Application (System Tray Service)

A lightweight Windows service that runs in the background. It:

When you click "Sync to Game" on the web app, it sends route data to http://127.0.0.1:38765/api/route. The helper receives it and writes to shared memory.

2. DirectX 12 Overlay DLL

A DLL injected into the game process that:

The DLL runs inside the game's rendering thread, so it has direct access to DirectX resources. This lets us draw UI without creating a separate window or overlay process.

3. Web App Integration

The React frontend detects if the helper is running and shows a "Sync to Game" button:

const [helperConnected, setHelperConnected] = useState(false);

useEffect(() => {
    // Check if helper is running
    fetch('http://127.0.0.1:38765/api/status')
        .then(res => res.ok && setHelperConnected(true))
        .catch(() => setHelperConnected(false));
}, []);

const syncToGame = async (route: RouteData) => {
    await fetch('http://127.0.0.1:38765/api/route', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(route)
    });
    
    showNotification('Route synced to in-game overlay!');
};

When clicked, the route transfers to the game in <50 milliseconds. No file exports, no copy-paste, no alt-tabbing.

Overlay UI: Clean and Contextual

The in-game HUD is designed to be informative but non-intrusive:

┌─────────────────────────────────────┐
│ Route: Jita → Amarr (15 jumps)     │
│                                      │
│ Next: J212103-Taru                  │
│ Distance: 4.2 LY                    │
│ ETA: 8 minutes                      │
│                                      │
│ Progress: ███████░░░ 7/15           │
└─────────────────────────────────────┘

Key design principles:

1. Minimal visual footprint: The overlay occupies <10% of screen space, positioned in the corner where it doesn't block gameplay.

2. High contrast: White text on semi-transparent black background for readability in any scene (bright nebula or dark space).

3. Toggle-able: Press Ctrl+O to hide/show. When hidden, our hook adds <0.1ms per frame—imperceptible.

4. Auto-updates: As you jump through gates, the overlay detects your new system (via game memory reading) and updates progress automatically.

Rendering Pipeline: ImGui in DirectX 12

We use ImGui (Immediate Mode GUI) for overlay rendering because it's lightweight and integrates easily with DirectX:

// Initialize ImGui (once, when overlay loads)
ImGui::CreateContext();
ImGui_ImplWin32_Init(hwnd);
ImGui_ImplDX12_Init(device, NUM_BACK_BUFFERS, 
    DXGI_FORMAT_R8G8B8A8_UNORM, cbvSrvHeap, 
    cbvSrvHeap->GetCPUDescriptorHandleForHeapStart(),
    cbvSrvHeap->GetGPUDescriptorHandleForHeapStart());

// Every frame (in Present hook)
void RenderOverlay() {
    ImGui_ImplDX12_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();
    
    // Read route from shared memory
    RouteData* route = g_sharedRoute.load(std::memory_order_acquire);
    
    if (route && g_overlayVisible) {
        ImGui::SetNextWindowPos(ImVec2(20, 20), ImGuiCond_FirstUseEver);
        ImGui::SetNextWindowSize(ImVec2(350, 150), ImGuiCond_FirstUseEver);
        
        ImGui::Begin("Route Navigation", nullptr, 
            ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
        
        ImGui::Text("Route: %s", route->route_name);
        ImGui::Separator();
        ImGui::Text("Next: %s", route->next_waypoint);
        ImGui::Text("Distance: %.1f LY", route->distance_remaining);
        ImGui::Text("ETA: %d minutes", route->eta_minutes);
        
        float progress = (float)route->current_jump / route->total_jumps;
        ImGui::ProgressBar(progress, ImVec2(-1, 0), 
            fmt::format("{}/{}", route->current_jump, route->total_jumps).c_str());
        
        ImGui::End();
    }
    
    ImGui::Render();
    ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), commandList);
}

This runs every frame, but ImGui is efficient—typical overhead is <0.5ms, which is acceptable at 60 FPS (16ms budget per frame).

Shared Memory: Fast Cross-Process Sync

The helper and overlay DLL are separate processes, so they can't share variables directly. We use Windows shared memory:

// Helper creates shared memory
HANDLE hMapFile = CreateFileMapping(
    INVALID_HANDLE_VALUE,
    nullptr,
    PAGE_READWRITE,
    0,
    sizeof(RouteData),
    L"Global\\EFMapRoute"
);

RouteData* pRoute = (RouteData*)MapViewOfFile(
    hMapFile,
    FILE_MAP_ALL_ACCESS,
    0, 0,
    sizeof(RouteData)
);

// Helper writes route data
memcpy(pRoute, &newRoute, sizeof(RouteData));

// Overlay reads route data
HANDLE hMapFile = OpenFileMapping(
    FILE_MAP_READ,
    FALSE,
    L"Global\\EFMapRoute"
);

RouteData* pRoute = (RouteData*)MapViewOfFile(
    hMapFile,
    FILE_MAP_READ,
    0, 0,
    sizeof(RouteData)
);

This allows sub-millisecond synchronization: when the helper receives a new route from the web app, the overlay sees it within one frame (16ms at 60 FPS).

Auto-Progress Tracking: Detecting System Changes

The most magical feature: auto-updating progress. As you jump through gates, the overlay detects your new system and increments the progress bar automatically.

We do this by reading game memory:

// Find current system ID in game memory
uintptr_t baseAddress = GetModuleBaseAddress("exefile.exe");
uintptr_t currentSystemPtr = baseAddress + CURRENT_SYSTEM_OFFSET;
uint64_t currentSystemId = *(uint64_t*)currentSystemPtr;

// Check if system changed
if (currentSystemId != lastSystemId) {
    lastSystemId = currentSystemId;
    
    // Update route progress
    for (int i = 0; i < route->waypoint_count; i++) {
        if (route->waypoints[i].system_id == currentSystemId) {
            route->current_jump = i + 1;
            break;
        }
    }
}

We only read game memory (never write), so this is safe and doesn't violate CCP's policies. It's the same technique used by tools like EVE-Mon and PyFa.

Performance: Zero Noticeable Impact

We benchmarked the overlay extensively:

At 60 FPS (16.67ms per frame), our 0.5ms overhead is 3% of the frame budget—imperceptible to players. We've tested on low-end systems (GTX 1060, 8GB RAM) and high-end systems (RTX 4090, 64GB RAM) with identical results.

User Feedback: Game-Changing Feature

Since launching the overlay, we've received incredible feedback:

"This is how navigation SHOULD work. I can't go back to alt-tabbing." — Fleet Commander, VOLT alliance
"The overlay reduced my hauling time by 20%. I can focus on piloting instead of checking maps." — Logistics pilot
"Finally, a tool that feels like part of the game instead of a separate app." — Solo explorer

The overlay has 8,000+ active users and a 78% weekly retention rate—higher than any other feature we've shipped.

Lessons for Building Game Overlays

Building this taught us several key principles:

1. Performance is non-negotiable. Any FPS drop is immediately noticeable. Profile aggressively and optimize hot paths.

2. ImGui is perfect for game tools. It's lightweight, easy to integrate, and designed for real-time rendering.

3. Localhost APIs beat custom protocols. HTTP is simpler than binary IPC and works with standard web tools.

4. Shared memory enables real-time sync. Cross-process communication needs to be <1ms for smooth UX.

5. Auto-tracking delights users. Detecting system changes automatically eliminates manual progress updates.

Future Enhancements

We're planning several overlay improvements:

The user overlay is our flagship feature—it's what makes EF-Map more than just a website. It's a seamless bridge between web-based route planning and in-game execution. And we're just getting started.

Want to try the overlay? Download the helper from the Microsoft Store and experience navigation reimagined.

Related Posts

directxoverlaygame integrationnative appsreal-time sync