Navigation Plugin V2 is here!

Hi everyone,

We’re excited to announce that the Navigation Plugin V2 has just landed in Babylon.js ADDONS! :tada:

This new version is built on top of the excellent recast-navigation-js library by Isaac Mason. It’s fully ES6-compliant, modern, and actively maintained — a big step forward from the legacy V1 implementation.

Why V2?

  • V1 relies on an outdated JS package that often breaks with ES6 and bundlers. V1 is going to be deprecated in the future.
  • V2 uses recast-navigation-js under the hood, offering better compatibility, stability, and performance.
  • V2 will eventually replace V1 after more testing, but you can already switch over easily — the interface is 100% compatible.

There is only one difference: V2 must be initialized with an async factory function instead of a constructor.

const navigationPlugin = await ADDONS.CreateNavigationPluginAsync(); // WASM under the hood
const navigationPlugin = await ADDONS.CreateNavigationPluginWorkerAsync(); // Worker version not yet supported, coming soon

What’s New in V2?

While keeping everything you know from V1, V2 adds a lot of powerful new tools and options:

New Navigation Mesh Parameters

V2 introduces more fine-tuning when generating a navmesh:

{
  offMeshConnections: IOffMeshConnection[],
  keepIntermediates: boolean,
  maxObstacles: number,
  expectedLayersPerTile: number,
  tileCacheMeshProcess: TileCacheMeshProcess
}

These unlock advanced features like dynamic obstacles, jump links, and debug visualization.

Per-query Options

Most plugin functions now accept an options object to tweak behavior per-call:

options?: {
  filter?: QueryFilter;
  halfExtents?: IVector3Like;
  // …more depending on function
}

This allows you to define agent-specific filtering and behaviors on the fly.

OffMeshConnections

Custom connections between points on the mesh, useful for:

  • Teleports
  • Jump links
  • One-way passages

Exposed Internals (for advanced use cases)

After creating a navmesh, you can now directly access:

  • navMesh (core mesh)
  • navMeshQuery (primary interface for queries & pathfinding)
  • tileCache (dynamic obstacles & updates of tile parameters)
  • intermediates (debugging & visualization)

Navigation Debugger

A brand-new interactive debugger overlay to:

  • Inspect navmesh tiles & layers
  • Visualize off-mesh connections
  • See intermediate build data (with keepIntermediates: true)
  • Tune parameters and immediately understand agent behavior

Raycasting & Path Utilities

  • Use navigationPlugin.raycast(start, end) for line-of-sight queries.
  • Apply new helper functions to post-process paths (L-shaped corners, smooth curves, etc.).

NavMesh generator beta

An interactive visual tool that lets you:

  • Fine-tune navmesh parameters directly on your scene meshes
  • Instantly preview the results
  • Export the navmesh as a compact binary file for lightning-fast production loads

Wrap-up

Navigation Plugin V2 is designed to be drop-in compatible for most existing use cases while offering deeper control, debugging, and performance improvements.

We’d love for you to try it out, share your feedback (you can directly tag me in your post), and help us battle-test it!

If you’re using V1 today, migrating should be straightforward. Give V2 a spin and let us know how it works in your projects!


You can already enjoy and try out Navigation Plugin V2 directly in the Babylon.js Playground!

The ES6 docs are under construction, and I’m actively preparing more example Playgrounds to showcase advanced scenarios and best practices — stay tuned!


Check out the docs:


Special thanks goes to @Cedric @ryantrem @RaananW @Deltakosh and to Isaac for the recast-navigation-js library!

Another chain shattered, another limit broken. The Force does not guide me… it obeys me. :sweat_smile:

24 Likes

Oh my goodness this is EPIC! Going to test it soon on my game project (that heavily uses the nav plugin v1 now…)! :star_struck:

I’m really REALLY interested in few things atm:

  • Hopefully plugin does not introduce unnecessary `new Vector3` stuff (minimal gc)
  • Mesh generation time compared to V1 especially in the null engine/headless environment (really important for me, since I generate random maps on the server and build them from mesh pieces, including the navmesh)
  • Queries to navmesh, are they faster than V1? Meaning the functions like getRandomPointAround, getClosestPoint, etc…
  • How does the raycast work and what are the usecases, where does it collide…
  • How does the plugin handle different size agents? Or further to elaborate; at least with V1 if I have two very different sized agents, like a human and a stone golem, I need to generate a separate navmesh for golem with larger padding. So is it the same with V2?

It is 30min past midnight here in Finland, I have to sleep but so eager to test this :smiley:

Ok few pointers with small testing:

  • Client does generate the navmesh but something is off with it so it does not work straight away, have to debug more later on. Looks like my combined map is being ignored or something :thinking:
  • Server throws error because of:
64 |             "@recast-navigation/wasm": `${baseUrl}/wasm@${version}/dist/recast-navigation.wasm-compat.js`,
65 |             // eslint-disable-next-line @typescript-eslint/naming-convention
66 |             "@recast-navigation/generators": `${baseUrl}/generators@${version}/dist/index.mjs`,
67 |         },
68 |     };
69 |     const script = document.createElement("script");
                        ^
ReferenceError: document is not defined
5 Likes

Thank you @roland for this huge contribution!! :tada:

1 Like

thanks buddy!

This was one of my concerns with the V1 plugin. I couldn’t break the V1 interface so V2 still returns a new Vector3. However you can use the toRef functions to avoid creating new objects. On the orher hand whereveer I could I changed the functions parameter from Vector3 to IVector3Like.

Since the recast-navigation-js must be injected dynamically (the core team didn’t agree to bundle the library with babylon.js - it’s totaly understandable) it must create a the script element.

I can add a parameter like this:

export async function CreateNavigationPluginAsync(options?: {
        instance: RecastInjection;
})

export type RecastInjection = typeof import("@recast-navigation/core") & typeof import("@recast-navigation/generators");

so you can include recast-navigation-js as a dependency in your package.json and inject the lib in the factory function.

I didn’t do performance tests but since the js part is more lighter (the WASM part is stripped to the bone as well) it have to be faster. If you worried about performance you can skip some of the plugin functions by using the exposed NavMeshQuery object directly.

Don’t calll this function:

     public getClosestPoint(
        position: IVector3Like,
        options?: {
            /**
             * The polygon filter to apply to the query.
             */
            filter?: QueryFilter;
            /**
             * Half extents for the search box
             */
            halfExtents?: IVector3Like;
        }
    ): Vector3 {
        const ret = this._navMeshQuery.findClosestPoint(position, options);
        const pr = new Vector3(ret.point.x, ret.point.y, ret.point.z);
        return pr;
    }

But do this instead:

const navMeshQuery = navigationplugin.navMeshQuery; // store this to avoig hitting the getter everytime you use the `navMeshQuery`
.
.
.

const result: IVecto3Like = navMeshQuery.findClosestPoint(position, options);

For example:
Line of sights check/shooting check: is there and obstacle between the player and an enemy for example, can it see the player, can the player shoot at it, etc…
Smoth steering: you can start to steer your agents even before they reach a non-navigatable part of the navmesh
NPCs moving randomly can avoid to start to walk into walls

I think this was an issue with the old recast-js lib and not with the plugin. Creating two navmeshes doesn’t make any sense. I’ll create a PG and test this.

If you can send me the map I’ll try to help you find out the issue. I tested several maps and didn’t have an issue yet.


Thank you very much for your comments! If you are in, we can tweak the plugin together and make it even better!

3 Likes

Isaac is also working on a new JS-native navmesh library GitHub - isaac-mason/navcat: javascript navigation mesh construction and querying library for 3D floor-based navigation So Navigation Plugin V3 soon? :grinning_face_with_smiling_eyes:

2 Likes

@Alexander_Sosnovskiy
Haha, you just dropped the bomb! :grinning_face_with_smiling_eyes: Yes, I’ve already started working on V3 after chatting a bit with Isaac about NavCat :smiley:

5 Likes

@Panuchka

This PR should allow you to run the plugin on the server.

3 Likes

I fully pledge myself to you Dark Rolious :man_bowing:
a close up of a man with the words " i pledge myself to your teachings "

4 Likes

@Panuchka has just joined the Dark Side

2 Likes

That’s so great! Congrats on the release!

3 Likes

Awesome and thanks a lot @roland :star_struck:
Looking forward to try out the OffMeshConnections :blush:

2 Likes

@Panuchka

I have to admit my answer is not correct. So you have two options here:

  1. create different navmeshes with different maxWalkableRadius parameter, uses more memory but the fastest way to query the navmesh
  2. you can use flags for certain polygons or offmeshconnections to prevent bigger agents to pass trough. You can the query the navmesh with these flags.
2 Likes

Dude! You freaking kicked ass with that one!!

Congratulations!

3 Likes

CAVEATS

Please take note that support for off-mesh connections is basic in recast-navigation and can hardly be used for anything else than demos.

  • It’s not possible to specify the cost for crossing the connection, which will be the euclidean distance between the endpoints. Pathfinding will not work well for teleports where the 2 endpoints are connected with an alternative walkable path.
  • With tiled meshes, it’s not possible to create connections that link distant (not-neighboring) tiles, which is problematic again for teleports
  • More important, the off mesh “animation” (including the traversal speed and trajectory) is hardcoded and can not be customized
  • There are other hardcoded settings with crowd agents like trigger radius for off-mesh connections (2.25 * agent radius), or slow down radius (the agents will slow down before reaching their destination)

None of these limitations come from recast-navigation-js but from the original c++ recast-navigation library, which I would not qualify as “actively maintained”. Isaac is doing a great job with this, and even better with navcat, which will be able to solve all mentioned points above.

4 Likes

It is what it is :frowning:

If someone is not satisfied with the built-in behaviour there are some options.

You can override the animation by your custom logic. I mean you can detect the agent on an off mesh connection. You can stop the agent and move it to the destination as you wish.

You mean if there is a walkable path to the destination and a teleport as well?

Maybe I don’t get something but:

Once again, yes it is what it is by default. However I believe you can write your custom logic to handle this as well.

We are looking forward for it to be released and use it under the hood of babylon.js’s navigation plugin :wink:

4 Likes

Yes. For instance, if you’re in-between 2 opposite teleports on a flat ground, it will only work if you’re really close to the teleport. The pathfinding will not prefer the path with the instant teleport because of the higher cost.

Last time I tried, I couldn’t get it to actually work, and it’s an issue that has been open for a long time

If “writing your custom logic” is rewriting DetourCrowd, after exposing missing IDL bindings in recast-navigation-js, then we do agree :sweat_smile:

Please, don’t be sarcastic :sweat_smile:. By “custom logic” I didn’t mean rewriting DetourCrowd. What I meant was skipping the built-in off-mesh connection handling entirely. I actually do this in my Digital Twin project: I move the agents between elevators (kinda teleports) exactly when and how I want.

Sorry, I do not mean to be sarcastic nor downplaying your work or anyone else’s work.

Surely you can tinker with it if you’re ready to have your off-mesh connection being triggered way farther than your actual endpoint.

My intention was just to add more details about a feature that is basically incomplete, before others dig into it and face the same disappointments than me. These caveats are not documented properly, and you can only find clues buried deep in github issues, discussions or code.

Ok, got it… No problem at all!

Appreciate it! Thank you!

@roland I’m happy to help however I can in exploring a navcat-backed navigation plugin :slightly_smiling_face:

As @SerialF mentioned, navcat already addresses many limitations of recast/detour discussed above:

  • Offmesh links are not tied to tiles but are added globally to a navmesh
  • Offmesh links can be created between any tiles, not just neighbouring tiles
  • Offmesh links can be added and removed dynamically
  • There for the forseeable term isn’t a DetourCrowd-like API abstraction. Instead, there’s a crowd simulation starter, where you can start customizing what offmesh link traversal looks like, how the desired velocity is actually enacted on your agents, etc

Additionally, because it’s written in javascript (it’s not subject to very poor performance for wasm → js calls) we can write normal js functions for filters deciding what polys and offmesh links should be walkable, and cost functions for traversing polys and offmesh links. This is a huge improvement in usability over having to fit your logic in Detour’s default query filter’s includeFlags & excludeFlags, and area cost lookup tables.

One thing worth calling out - IMO navcat’s reduced level of abstraction has benefits. Instead of working with rigid abstractions, you get tools to build what you need, and starter examples to get going quickly with them. If you want to fine tune how agent movement works, or you want to write a custom search on the navmesh specific to your game (e.g. maybe you want to do flow field pathfinding for hordes of enemies), you can do it. You don’t necessarily need to open a PR for a new API abstraction.

Maybe it’s worth evaluating for babylonjs whether exposing more of these underlying tools would be useful. It’s already possible to implement the existing bablyonjs navigation interface with navcat (most of the API already has or will soon have corresponding examples). But if all bjs did for a NavigationPluginV3 is swapped over to navcat with the same interface, probably lots of benefits won’t be recieved by users.

Happy to discuss this more :slight_smile: Let me know where is easiest for us to chat back and forth if you’re keen to collaborate!

10 Likes