Performance problems for tactical graphics

When you are displaying tactical graphics on a WebGL map, you may run into performance issues. These issues crop up because shader compilation for tactical graphics is a time-consuming process:

  • Loading many different tactical graphics simultaneously might freeze the browser. The severity of this problem strongly depends on your operating system, your browser, and the number of distinct tactical graphics you are using.

  • Selecting or editing a tactical graphic leads to a noticeable delay because the selection style requires the compilation of a new shader.

Possible solutions

Faster shader compilation

Starting up Chrome using the openGL flag
chrome.exe --use-angle=gl

Improving the user experience

You can make use of UX patterns to ease the performance problem.

Shader compilation is needed only when a tactical graphic is rendered completely accurately, with complex strokes and decorations.In the remainder of this article, we refer to this type of rendering as body. You can also start off with a simplified way of rendering, with simple lines and without decorations. We call this new way of rendering skeleton from now on.It doesn’t require shader compilation.

  • You can indicate that you want skeleton rendering by setting MilitarySymbologyPainter.MilSymStyle.body to false and MilitarySymbologyPainter.MilSymStyle.skeleton to true on your

  • You can make all your style preferences in dependent on FeaturePainter.PaintState.

    Figure 1. Body versus Skeleton styling (and the effect upon selection)

    For example, Program: Defining PaintState-dependent styling properties illustrates how to provide the skeleton style for layer pre-loading and for selection in the MilitarySymbologyPainter.MilSymStyle.

    Program: Defining PaintState-dependent styling properties. (from samples/allsymbols/main.tsx)
        style: (feature, paintState): MilSymStyle => {
          return {
            selectionColor: "#FF9900",
            // The body will not be drawn if there is a feature property "body" that is set to false.
            // If so, a skeleton (tactical graphic without details and decorations) will be drawn.
            body: ?? true,
            // The skeleton will be drawn if there is no body, or, as a selection style on top of the body
            // on WebGL maps (this allows immediate response upon selection).
            skeleton: !( ?? true) || (paintState.selected),
            // A selection rectangle will be drawn for icons when selected.
            rectangle: paintState.selected,
            affiliationColor: {
              Friend: || FRIEND_COLOR,
            haloColor: (paintState.hovered && !paintState.selected) ? "rgba(255,255,255,0.7)"
            haloWidth: (paintState.hovered && !paintState.selected) ? 2 :,
  • If you pre-load your tactical graphics in a skeleton style, you can immediately start working on the layer. You can edit tactical graphics in the skeleton style in the same way as you would edit them in the full body style.

  • After pre-loading, on Layer.whenReady, you can invalidate the features one by one, and let them re-paint with the body style preference.

    Handle the features one by one to keep your browser from freezing while compiling shaders.

    The code that make this possible is available as sample code in SymbologyProgressUtil.

  • You can also use the skeleton style to visualize a selected symbol. This allows for immediate response upon selection.

Using the progress indicator in the LayerTree, you can keep track of the replacement rate of a skeleton -styled tactical graphic with a body-styled tactical graphic.