# Updating mapbox layer is causing a strange memory issue leading to flickering

**URL:** <https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856>\
**Category:** Help\
**Created:** [July 26, 2022, 3:53pm UTC](https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856 "2022-07-26T15:53:09Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![mbrownshoes](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbrownshoes/32/423_2.png) [@mbrownshoes](https://talk.observablehq.com/u/mbrownshoes)\
**Post date:** [July 26, 2022, 3:53pm UTC](https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856/1 "2022-07-26T15:53:09Z")

</div>

Hi everyone,

I’ve tried to title this topic appropriately, but here I’ll explain more clearly the issue I’m encountering.

Recently, I decided to update a static map that uses circle packing ([https://ico.hakai.org/](https://ico.hakai.org/)) with something that is zoomable. I figured out how to recreate the circle packing map using mapbox as the basemap. Of course, this meant that as you zoom in the circle packing function would have to run repeatedly to update the circle positions. Using some existing examples on Observable I was able to accomplish this outcome. However, the map also includes a brush, to allow the user to filter on particular date ranges.

While, the brush seemed to work at updating the circle positions, without having to reload the basemap, I found that when I zoomed, after a few different data ranges had been selected, the circles would flicker and briefly showed the positions of previously selected date ranges.

I tracked it down to the output of the brush, which seemed to update correctly when used, but when zooming on the map, and printing the brush values to the console, I could see the old values being printed to the console.

It’s a bit hard to explain so I’ll post the actual notebook here, which tries to walk you though the problem.

> **[Mapbox flickering issue](https://observablehq.com/d/c5152324a235c2c1)**
>
> The brush below filters the data used in the circle packing map below. Circle packs are recalculated as we zoom in and out. Below we can see the limits values seem correct.. However, if we move the brush around several times to select various date...

When first loaded the zooming works well, but move the slider around a few times and then try zooming. I know there is a lot going on here! I’ve run this notebook through the Observable notebook visualizer and there didn’t seem to be any weird dependancies going on…

Thanks!

---

<div class="post-metadata">

**Author:** ![mcmcclur](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mcmcclur/32/2364_2.png) [@mcmcclur](https://talk.observablehq.com/u/mcmcclur)\
**Post date:** [July 26, 2022, 4:31pm UTC](https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856/2 "2022-07-26T16:31:37Z")

</div>

Here’s a [forked version](https://observablehq.com/d/cd8a9d2bcdd059d9) that seems to run more smoothly after adjusting the brush, though it doesn’t seem to help the flicker. I actually accomplished this by forking the [Brush Filter X notebook](https://observablehq.com/@observablehq/brush-filter-x) and adjusting that code so that the brush only updates on release. This could clearly be improved since I doubt that it eliminates the leak; rather, it just limits it to one leak per adjustment. It looks to me like you’re using invalidation correctly so I don’t get that.

This is a great question, by the way, I’d love to see it more properly resolved.

---

<div class="post-metadata">

**Author:** ![mbrownshoes](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbrownshoes/32/423_2.png) [@mbrownshoes](https://talk.observablehq.com/u/mbrownshoes)\
**Post date:** [July 26, 2022, 4:44pm UTC](https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856/3 "2022-07-26T16:44:08Z")

</div>

Nice that it only updates on release but yeah the flickering seems just as bad. Thanks for looking it over. Hopefully there’s someone who can help resolve this.  
Cheers

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [July 26, 2022, 7:08pm UTC](https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856/4 "2022-07-26T19:08:33Z")

</div>

Without looking at the notebook in more detail, I would suspect one of the following causes:

- You’re recreating the WebGL context on each change. In this case you would have to at least ensure that the previous instance is destroyed to free up its resources. A good indicator for this are warning about too many WebGL contexts.
- You’re adding layers / elements instead of updating, so that the amount of things that needs to be drawn increases with every change.

---

<div class="post-metadata">

**Author:** ![mbrownshoes](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbrownshoes/32/423_2.png) [@mbrownshoes](https://talk.observablehq.com/u/mbrownshoes)\
**Post date:** [July 26, 2022, 8:16pm UTC](https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856/5 "2022-07-26T20:16:20Z")

</div>

I don’t know. I have not seen any errors about too many WebGl contexts. To update the layers I’m using `setData` and then remove the layer before I add the new one. It works just fine if the brush doesn’t change the dates filtered on, but once I have a new date range to filter on, somehow the previous dates are being stored somewhere… The `updateLayer` function still seems to be removing and adding layers as I zoom.

I should be clear that, as far as I can see, the reason it’s flickering is because that on zoom, a previous data range filters the data differently, resulting in circles appearing in a different spot for an instant.

---

<div class="post-metadata">

**Author:** ![chrispahm](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/chrispahm/32/4203_2.png) [@chrispahm](https://talk.observablehq.com/u/chrispahm)\
**Post date:** [July 27, 2022, 9:55am UTC](https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856/6 "2022-07-27T09:55:59Z")

</div>

Hey @mbrownshoes !

First of all, that is a stunning map you made there! 🎉

The reason your map flickers is that the zoom/viewreset/move event listeners are added to the map with every input to the brush input view. That’s happening because the event listeners are defined in the cell that also defines the `updateLayer` function. By (the current) design, the `updateLayer` cell gets updated with every input to the brush view, and that’s why more and more event listeners are attached until the map starts flickering.

In practice, if you scrubbed the brush 50 times, a single zoom event will cause your `updateLayer` function to be run 50 times as well (yielding the exact same result with every iteration though).

There’s a simple hack you can use to circumvent this: If you add a mutable cell like the following to your notebook

```nohighlight
mutable countReEvaluation = -1

```

and adjust the `updateLayer` cell in the following manner

```nohighlight
{
  mutable countReEvaluation++;
  const updateLayer = () => {
    // ...
  }
  if (mutable countReEvaluation <= 0) {
    map.on("viewreset", updateLayer);
    map.on("move", updateLayer);
    map.on("moveend", updateLayer);
  }

```

The event listeners are only added to the map once!

While the above might be a simple hack that doesn’t require much refactoring, I would generally propose to use the “standard” approach for listening to input events non-reactively within a cell

```nohighlight
viewof yourInput.addEventListener('input', () => {
      const curValue = viewof yourInput.value;
}

```

Here’s a simple implementation of that event listener from your Bear Tracker notebook 😊

> **[Bear tracker with scrubber](https://observablehq.com/@chrispahm/bear-tracker-with-scrubber)**
>
> Non-scrubber version

---

<div class="post-metadata">

**Author:** ![mcmcclur](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mcmcclur/32/2364_2.png) [@mcmcclur](https://talk.observablehq.com/u/mcmcclur)\
**Post date:** [July 27, 2022, 1:13pm UTC](https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856/7 "2022-07-27T13:13:21Z")

</div>

Nice analysis @chrispahm! I updated [my fork](https://observablehq.com/d/cd8a9d2bcdd059d9) to implement the simple hack and it works well. In addition, there’s no longer any need to use the brush filter that only updates on release so I reverted to the original brush.

---

<div class="post-metadata">

**Author:** ![mbrownshoes](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbrownshoes/32/423_2.png) [@mbrownshoes](https://talk.observablehq.com/u/mbrownshoes)\
**Post date:** [July 27, 2022, 3:42pm UTC](https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856/8 "2022-07-27T15:42:48Z")

</div>

Thanks so much for your reply, and the detailed explanation of the issue. I’ve tried implementing your hack approach first, and while it does solve the issue of the flickering, I’m finding that after scrub the brush and then zoom, the original circle positions appear on the map, rather than the ones that should appear for the brushed dates.

It seems this is due to the `limits` dates in the `updateLayer` function being set to the defaultExtent of the brush. You can see this with the `mutable debugLimits` not updating to the current limits dates when zooming. Am I correct in assuming that the limits dates in the `UpdateLayer’ function are frozen at the original dates provided?

I’ll need to update the data within the `updateLayer` function when I zoom, and I’m not sure how to do that with either approach you mentioned.

Here is an updated notebook implementing the hack approach. Filtering seems to work but when you zoom, the original circles show up…

> **[Mapbox flickering issue](https://observablehq.com/d/f7b1266458e4b786)**
>
> The brush below filters the data used in the circle packing map below. Circle packs are recalculated as we zoom in and out. Below we can see the limits values seem correct.. However, if we move the brush around several times to select various date...

---

<div class="post-metadata">

**Author:** ![mbrownshoes](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbrownshoes/32/423_2.png) [@mbrownshoes](https://talk.observablehq.com/u/mbrownshoes)\
**Post date:** [July 27, 2022, 9:07pm UTC](https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856/9 "2022-07-27T21:07:42Z")

</div>

Okay, I think I’ve got it. I found this post very helpful

> [@Closure bug with map click handler](https://talk.observablehq.com/t/closure-bug-with-map-click-handler/2948/2):
>
> The cell that adds the load listener is running whenever the inputText changes, which is whenever you edit the text in the textarea. { map.on('load', () =\> map.on('click', evt =\> { console.log('clicked', inputText); }) ); } Each time it runs, it’s registering a new load event listener—in addition to all the previous ones. But since the map is already loaded, the new listener is never invoked, and so never registers another click event listener, and so only the first listener …

I made the updateLayer function an individual cell, and in another cell I put this:

```auto
{

  map.on("viewreset", updateLayer);
  map.on("move", updateLayer);
  map.on("moveend", updateLayer);
  invalidation.then(() => {
    map.off("viewreset", updateLayer);
    map.off("move", updateLayer);
    map.off("moveend", updateLayer);
  });
}

```

I had tried previously using `map.off` to remove the old listener but I only did it for `mapend`. I hadn’t realized that I needed to remove each of the viewreset, move and movened events.

Here is the updated notebook. [Mapbox updates on zoom / Mathew Brown | Observable](https://observablehq.com/d/f7b1266458e4b786)

Thanks @chrispahm and others for your help solving this problem! I was stumped!

---

<div class="post-metadata">

**Author:** ![chrispahm](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/chrispahm/32/4203_2.png) [@chrispahm](https://talk.observablehq.com/u/chrispahm)\
**Post date:** [July 28, 2022, 7:12am UTC](https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856/10 "2022-07-28T07:12:07Z")

</div>

Awesome! Sorry I missed out on that closure issue, but good to hear you could solve it!

---

<div class="post-metadata">

**Author:** ![mbrownshoes](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbrownshoes/32/423_2.png) [@mbrownshoes](https://talk.observablehq.com/u/mbrownshoes)\
**Post date:** [July 28, 2022, 2:56pm UTC](https://talk.observablehq.com/t/updating-mapbox-layer-is-causing-a-strange-memory-issue-leading-to-flickering/6856/11 "2022-07-28T14:56:22Z")

</div>

No worries, thanks for figuring out what the issue was!
