CodePen on mobile = buttery smooth; Webflow on mobile = stutters / skipped frames when scrolling near SVG image

This bug only exists on medium-end iPhones on Webflow. Desktop, tablets, newer iPhones (e.g., iPhone XR), and medium-end iPhones on CodePen are all fine.

Steps to Reproduce

  1. Open Safari on your medium-end iPhone.
  2. Open this Webflow page & scroll using a flick (i.e., scroll and then let go)
  3. Open this CodePen.io page, hide HTML, & scroll using a flick (i.e., scroll and then let go)

Expected Result: both sites scroll with similar performance

Actual Result: CodePen is buttery smooth throughout, while Webflow stutters when the SVG is in the viewport

Poorly Done Video recording: iPhone 6S Plus 128 GB, iOS 13.3, Safari in Private

What might be the issue here? Surely, if the phone can render the scroll smoothly on CodePen, the phones should have no issue scrolling on Webflow. Have I reproduced the page properly in CodePen? I think so, but I could be wrong.

I’d love for this to be a simple issue.

The frustration stems from the SVG working perfectly on CodePen, but I can’t get anywhere that kind of performance on Webflow.

What I’ve tried already

  • Raster images scroll fine (though at 10x to 30x the file size, especially on HiDPI devices)
  • Very simple SVGs scroll fine
  • Setting preserveAspectRatio to slice vs none on the SVG = the bug remains
  • Restarting the iPhone = the bug remains
  • Optimizing the SVG with SVGOMG & Vecta.io’s Nano = the bug remains
  • Setting mix-blend-mode to normal = the bug remains
  • Testing an iPhone XR (Webflow scrolls fine); testing an iPad Air 3rd gen (Webflow scrolls fine)

Here is my site Read-Only: LINK

Before anyone asks, please do open the Read-Only link given above and LINK HERE again. There are zero Interactions used.

Loading benchmark:

Webflow page:

2020-01-21 14_53_44-laggy-svg-on-iphone
CodePen page (fullscreen):

2020-01-21 14_53_52-Image Test (Webflow)

This is on desktop. But relative performance, funny to see that there is no big difference. On to benchmarking the scroll action:

In a 500ms scroll down I collected this data for the webflow page:

2020-01-21 15_03_56-laggy-svg-on-iphone

And this data for the codepen page:

2020-01-21 15_05_40-Image Test (Webflow)

As you can see, the webflow generated page is not a light as the codepen. What does this mean? Well, for starters it’s to be expected. Webflow does come with it’s own javascript files on render. Those are pretty useless in your example but for actual websites they pack the basics.

Nothing too weird here. So why does the phone seem to lag? Could it be THAT sensitive to this javascript overhead? Maybe. But if it is, it should have this on virtually all major websites. Most of those are pretty heavy on the javascript. That’s why it’s a default browser and phone benchmark. So I find it odd to conclude webflow is in the wrong.

That’s why I went into the rendered code for both. I examined both webflow and codepen in how they use js and css. And I think I found why the codepen is faster here:

Webflow renders a nice code package, very accessible and readable. Perfectly acceptable. It relies on multiple calls to css and js, all industry standards.

Now there’s codepen. And what codepen did was weird at first. You see, codepen unpacks everything from the js and css files and dumps it all into 1 page in an iframe, with all styles and code dumped in-document. It then preloads all styles (wheter you need them now or not) and braces for interaction. It’s messy, it’s hard to read and to debug, BUT for the browser it’s faster because it does not have to consult multiple sources to get the info. It also explains why the script and render times were so low on the codepen, since it does that more agressively.

Now, this is all fun and games and codepen has a way to make the chunks of code people submit, display really fast. Would this approach be ideal for larger scale - say a website or an ecommerce? At the moment, the industry concensus is: No. extracting and injecting like this for target machine is error prone when you have a larger codebase or special libraries (which webflow -and basically everyone- does!)

It’s a very technical discussion for which you must have brown glasses and a dirty beard to be legit, but this kind of build manipulation has it’s place in the industry. But it’s not in the standard deployment way of doing business.

Fun fact; I remember back in the early 2000s this was something a lot of ‘webmasters’ in ecommerce were experimenting with. To gain that half second faster time to first draw, which was supposed to make or break your ecommerce.

Ah, those were not the days.

Thank you for testing, but…I think your hypothesis is talking about network performance: are you suggesting Webflow is re-rendering the SVG every time it enters the viewport? That would be bad practice, anywhere…

Webflow calls the SVG, the JavaScript is executed, and then the page finishes loading: that’s that. The same with CodePen. There should be no JavaScript execution (nor re-rendering of CSS/SVGs) on scrolling, unless of course, you’re suggesting the SVG is being re-rendered by Webflow?

Another issue is testing on desktop (which masks any serious performance issues); because there’s not been an adequate explanation by Webflow or anyone yet, I’ll benchmark the sites on Safari iOS later today.

Either way, it would definitely be a bug on Webflow’s part that scrolling on an essentially blank page causes JavaScript execution and/or re-rendering of an SVG.

Well yes and no. Network plays it’s part. But after load, I’m talking about browser render in realtime. You might not expect it, but your browser also needs to fresh each frame it draws on scroll. That refresh - or frame render - happens on the device and it’s speed is determined by scripts, css, etc.

An SVG needs to be drawn from RAM once loaded and into view. But somehow I don’t think an SVG is rasterized in-memory just like a jpeg for example. It’s quite possible an SVG is redrawn every time you scroll it out and into frame, or resize window. From what I can google; the SVG (due to being code) is kept in memory as code and generally has te be recalculated + redrawn on every frame - frame here beging anything other than current loaded state, hands off.

If you swap out the svg for jpeg, does it still have this issue?

Edit: Your svg is pretty light though. It should not be an issue. Unless the browser finds your geometry hard to render. Due to layers or overdraws or alikes. Can you bench on iOS with xcode or something?

Well webflow isn’t, your browser is. And from what I can read on the web this is indeed the case; The vectors are calculated for each viewport change. A “small” SVG of 1 MB can still be hugely complex to render for browser in terms of geometry, while a 1 MB PNG is super easy to just raster display. Does not need recalc.

This is also an interesting read on the topic: html - Are svg images harder to render for a browser than bitmaps? - Stack Overflow

1 Like

As stated in the OP, it’s not reproducing with raster images or even higher-end mobile devices: that’s settled.

Resizing, I agree, is a different ballgame: the entire site is often re-rendered. But, scrolling? That’s what’s got me lost.

The difficulty is Webflow can’t draw an SVG with the same performance that CodePen (of all places) can draw an SVG? The exact same SVG is being used, but let me investigate that, too: maybe there’s some old caching somewhere. I don’t think the “preloading all the styles” or “multiple sources” explains this discrepancy.

However, though this is no indication of how Safari works, Chromium shows no repaint on the SVG or the entire site as scrolling:

https://streamable.com/mzvb0

  1. Re-paint only occurs on resizing
  2. After loading the page & rendering the SVG once, scrolling doesn’t cause re-painting of the SVG or any divs (except the scrollbars, which is to be expected)

But, you’re right that SVGs are definitely more “CSS/HTML”, so I’ll try stripping the SVG down to its bare parts. Ideally, we’d not need to re-optimize SVGs just for Webflow, but it’s what I can do now: unfortunately, the iOS Safari rendering benchmarks can only happen in the afternoon as I’ve realized nobody has brought a Mac OS device to the office.

True: it is a light SVG, though it does use CSS color dodge. Let me go a little deeper.

EDIT: to the StackOverflow post, I’d say the same. True: the SVG does need to be drawn, path by path, by the browser (and not just output like a raster image)…but that’s done just once, when the page loads.

Curiously, the fix was actually changing the div’s position…hmm, I’ll give it a shot.

The site shouldn’t be triggering any repainting on scrolling. Resizing, yes: because different parts of the SVG are visible, so it must be re-drawn, but scrolling…itself shouldn’t do anything.

And yes: uploading a 1MB SVG will just tank a site! I’ve tried to optimize this SVG significantly, but let me bring it down to just the paths…

You just reminded me of something else that is pretty unique to iOS! This brought some headaches in the past. I’m not sure if Apple still does this (this was back in 2017) but on iPhone - for apps and safari - The current frame was (is?) NOT buffered or cached. Once you scroll out of view, it drops everything that’s out of view from memory.

Interesting…but, again, wouldn’t that also apply to CodePen, too? If anything, I’d expect iframes to be thrown out of cache even faster.

It also doesn’t explain the scrolling down jitter: the image is still in the viewport, but Webflow still stutters. The SVG is still within view, that is, and Webflow is still janky. In honesty, that is actually the worst place for jank because it’s the first experience for a new website visitor: the first interaction most users have with a website is to scroll, so you’ve already hit a snag there.

I’ve tried the minimalist SVG and, like my other SVG tests (i.e., solid color), reducing the complexity of the SVG does reduce Webflow jank, but, there are still dropped frames: nothing like CodePen, where even the “full-fat” SVG works flawlessly.

It should also apply to codepen yes. That’s why I am stil puzzled. Codepen does unpack the whole shebang and stuffs it into an iframe. Maybe that’s a way around it? But I didnt dive deep enough to know if that’s true. Maybe they even have a redraw cache or buffer mechanism running? It wouldnt be the worst idea, a lot of codepens are highly visual and I can imagine they would have a piece of javascript running regulating the draws.

Those scripts exist in all kinds of forms. This is what I could conjure up in a few seconds: Avoiding unnecessary paints

it would explain the jitter while the SVG is in view though. Remember that -without custom SVG caching mechanisms- the browser will redraw the SVG on scroll.

Edit: Apologies if I mix up paints and draws. I’m doing a lot of realtime 3D so the terminology goes mixed here

I’d asked Webflow support over the weekend and the answer was “Make a forum post. We don’t deal with rendering issues.” That’s…just beyond comprehension and seems to suggest an inability to read support tickets (or confusion).

While I think paint/draw may be different in realtime 3D, here they’re mostly referring to the same thing here in browsing: to re-iterate, the browser is not re-rendering / re-painting / re-drawing anything on scroll on this page (sure, your GPU is, but that’s even when you don’t scroll, unless we’re talking about panel self-refresh).

Please read the website this paragraph, in that very link:

In order to see what areas are being repainted you can use the “Show Paint Rectangles” feature in Chrome’s DevTools (just hit the little cog in the lower right corner). Then, with DevTools open, simply interact with your page and you’ll see flashing rectangles show where and when Chrome painted a part of your page.

I believe I just tested that in the post prior: there is no painting / redrawing with Chromium upon scrolling. It’s really not. Safari may be another question, however.

That article is talking about the “crazy” webdevs who first add dozens of interactions, hover events, animations, triggers, etc. and then second ask, “Why is it scrolling slow? Why can’t I hit the 16.67ms frametime budget consistently?”

There’s nothing like that here: I’ve specifically not added a single interaction here, no animations, no hover events. :frowning:

The only thing remaining, until I can test iOS Safari later today, is the somewhat different color rendering in the SVG by CodePen and Webflow, perhaps related to CSS’s color dodge. That also doesn’t make sense…as they’re both rendered by Safari.

This might be because Webflow’s SVG renderer works differently than CodePen’s SVG renderer, noted here: Displaying SVG in Web Browsers

Using the svg element you can also generate SVG directly in the browser using JavaScript. The D3 JavaScript API is very good at that. The jQuery JavaScript API could do that too.

Maybe Webflow uses D3 vs jQuery to render the SVG, while CodePen uses the opposite.

EDIT: I appreciate the replies and suggestions, @Ozone. Let me step away for a little while and settle my mind. I think I’ve been staring at this too long, while your suggestions are leading me closer to the solution .

1 Like

Cool! Let me know if you figure this out! Even if it’s just a direction to look into. I find this interesting as well :smiley:

1 Like

I’d like to welcome Dave from Webflow Support. To just briefly catch up:

  • There are zero divs set to position:fixed anywhere on the page.
  • There are zero interactions anywhere on the page.
  • This has nothing to do with the network, batched requests, HTTP latency, preloaders, etc. These issues occur after the page has loaded. Stutters while the page loads are 100% normal.

I was able to run significant testing directly on iOS Safari (not Chrome, not MacOS Safari, etc.) and I discovered key answers to this stutter. First, it is 100% reproducible and 100% throwing errors from Webflow’s JavaScript. Second, the bug is always present when using SVG, even on new projects, but the severity of the stutter is less on new projects. Finally, the bug is directly tied to code line 8037, dealing with w-lightbox, w-lightbox-content, and w-lightbox-view. (…meanwhile zero lightboxes used on this page).

In conclusion, I’ve found a lot of clues, but the actual resolution will need to be from Webflow properly as I’m wading out of my depth, trying to analyze this multi-thousand line JavaScript file.

Screenshot 1: scrolling through the page; one red notch under layout & rendering = one stutter

I’ve zoomed into a single stutter event at 2.13s. Anything with width 414px & height 512px = that’s the SVG. The problem is, during scrolling, the styles are recalculated, the layout is invalidated, and a re-paint has been ordered. That’s no good…why? Why is the CSS changing on scroll? Why is the layout being invalidated on scroll? Those will answer why a repaint has been done.

Screenshot 2: what’s triggering the CSS styles to be invalidated?


They consistently reference webflow.573bd23e0.js:8037. The JavaScript file can be opened here, but I’ve copied the relevant unminified section below:

Screenshot 3: line 8037 in Webflow’s JavaScript?

, function() {
                var n=t.navigator.userAgent, r=n.match(/(iPhone|iPad|iPod);
                [^OS]*OS (\d)/);
                if(n.indexOf("Android ")>-1&&-1===n.indexOf("Chrome")||r&&!(r[2]>7)) {
                    var i=e.createElement("style");
                    e.head.appendChild(i), t.addEventListener("resize", o, !0), o()
                }
                function o() {
                    var e=t.innerHeight, n=t.innerWidth, r=".w-lightbox-content, .w-lightbox-view, .w-lightbox-view:before {height:"+e+"px}.w-lightbox-view {width:"+n+"px}.w-lightbox-group, .w-lightbox-group .w-lightbox-view, .w-lightbox-group .w-lightbox-view:before {height:"+.86*e+"px}.w-lightbox-image {max-width:"+n+"px;max-height:"+e+"px}.w-lightbox-group .w-lightbox-image {max-height:"+.86*e+"px}.w-lightbox-strip {padding: 0 "+.01*e+"px}.w-lightbox-item {width:"+.1*e+"px;padding:"+.02*e+"px "+.01*e+"px}.w-lightbox-thumbnail {height:"+.1*e+"px}@media (min-width: 768px) {.w-lightbox-content, .w-lightbox-view, .w-lightbox-view:before {height:"+.96*e+"px}.w-lightbox-content {margin-top:"+.02*e+"px}.w-lightbox-group, .w-lightbox-group .w-lightbox-view, .w-lightbox-group .w-lightbox-view:before {height:"+.84*e+"px}.w-lightbox-image {max-width:"+.96*n+"px;max-height:"+.96*e+"px}.w-lightbox-group .w-lightbox-image {max-width:"+.823*n+"px;max-height:"+.84*e+"px}}";
                    i.textContent=r
                }
            }
            (), h

I’ve no idea what this code does, what it’s doing that needlessly causes layout invalidations and SVG repaints, nor what can be done to prevent the stutters it’s ostensibly causing.

However, I can illustrate the effect it’s having:

Screenshot 4: big frametime stutters, from 1ms average to suddenly 14+ms spikes

Screenshot 5: from loading the page—note all the network requests at the beginning—to the continuous layout invalidation and CSS recalculation

Screenshot 6: every style recalculation can be matched to a stutter (10.81s highlighted)

Screenshot 7: and every dang time, it’s the same line in the same Webflow JavaScript: webflow.573db23e0.js:8037

Screenshot 8: in a brand-new project, I’ve duplicated the page. The error, bug, and stutters STILL exist, but to a lesser degree. The stutter is easily noticeable, but not as egregious as the original project. No explanation, but my only guess is that the initial project actually has CSS classes, all of which may need to be re-parsed when the styles are invalidated:

Screenshot 9: the insane CPU usage spikes when scrolling on such a basic page, when the CSS is invalidated, the layout is recalculated, all triggering a re-paint

Screenshot 10: somehow, CodePen’s page does NOT show the same issues: no recalc, no invalidation, no repaint

1 Like

Other troubleshooting I’ve done, with no difference:

  • move the image to the middle of the page = stutters remain
  • move the image to a scrollable frame, to avoid triggering iOS Safari’s shrinking header, a la CodePen = stutters remain
  • viewing Webflow via an iframe on CodePen = no stutters
  • added FastDOM script site-wide (unknown compatibility w/ Webflow’s JS) = stutters remain

The iframe test: maybeWebflow’s buggy JS doesn’t/can’t activate when on an iframe on CodePen’s site? The FastDOM script has been removed for saner troubleshooting.

Some research I’ve done:

This issue is apparently ‘layout thrashing’:

Layout Thrashing occurs when JavaScript violently writes, then reads, from the DOM, multiple times causing document reflows.

When the DOM is written to, layout is ‘invalidated’, and at some point needs to be reflowed. The browser is lazy and wants to wait until the end of current operation (or frame) to perform this reflow.

Although, if we ask for a geometric value back from the DOM before the current operation (or frame) is complete, we force the browser to perform layout early, this is known as ‘forced synchonous layout’, and it kills performance!

The side effects of layout thrashing on modern desktop browsers is not always obvious; but has severe consequences on low powered mobile devices.

I don’t know whether their fix can work with Webflow’s JS, but they have a test site showing an exaggerated effect:

https://wilsonpage.github.io/fastdom/examples/animation.html

Compare “FastDom” vs “Forced synchronous layout”. More reading from Google here and aFasterWeb here.

I’m still investigating on the SVG side, but it’s happening even with a nearly barren SVG: no gradients, no CSS color blend: just the circular paths. Though, again…why should we be optimizing images that work well on CodePen, but won’t on Webflow?

@TG2 Thanks for the deep dive. It seems that you may have pinpointed something with the code snippet that you found triggering repaints. The code that you highlighted does a few things:

  1. On certain versions of iOS, there are limitations in how we can set up the lightbox styling and sizing
  2. So as a workaround, we measure the screen on resize and generate some CSS
  3. Scrolling iOS Safari can trigger resize, because the address bar shrinks as you start to scroll

There are a few things that we can look at for fixing this issue.

  1. Don’t load lightbox js if you’re not using lightbox on the site
  2. Do the CSS generation less often, causing less repaints

I can’t guarantee a timeline to work on this. But we really appreciate the time that you put into this report.