Ir al artículo
← volver a la ingeniería

Electron no es el cuello de botella de tu grabador de pantalla

Ilustración de portada para Electron no es el cuello de botella de tu grabador de pantalla

TL;DR — Electron has a real memory cost. It does not have to impose a meaningful recording or export penalty.

On our M4 Mac mini, a native ScreenCaptureKit recorder and the same native recorder hosted from Electron produced 4K video at 57.70 and 57.53 fps. A 4K editing shader took 1.62 ms per frame at p95 in direct Metal and 1.90 ms through Electron’s WebGL-to-Metal path. A controlled 1080p H.264 export ran at 7.54x real time through native VideoToolbox and 7.46x through Electron’s WebCodecs path.

The gap was not zero. It just was not where the conventional argument puts it.

Electron’s clear loss was memory: 45 MB for the direct native recording host versus 94 MB for the signed Electron/Node host, before adding a Chromium renderer. A packaged Tight Studio session settled around 744–749 MB in this test. That is a trade-off worth naming honestly. It is not evidence that captured frames must travel through a web page or that export must fall back to software encoding.

We build Tight Studio with Electron, so we have an interest in this question. That is why we made the benchmark controlled and repeatable, and why this post includes the results that make Electron look worse as well as the ones that make it look good.

Diagram showing Electron handling product controls while native capture, Metal-backed GPU rendering, and hardware codecs handle the media paths

“Native vs Electron” is the wrong level of abstraction

A desktop app is not one execution engine.

Electron supplies a Chromium renderer, a Node.js main process, and a multiprocess application shell. It also supports native modules. Electron’s own documentation is explicit that an app can use native code for platform APIs and performance-critical work.

That means an Electron screen recorder can be built in at least two very different ways:

  1. Capture, composite, copy, and encode frames through JavaScript and browser surfaces.
  2. Use Electron for product UI and orchestration while ScreenCaptureKit, AVFoundation, Metal, VideoToolbox, or another native media engine owns the hot path.

Those apps share a framework label. They do not share a performance architecture.

Electron already powers demanding desktop apps

This architecture is not unusual. The current macOS distributions of Codex and Claude use Electron. On the machine used for this article, Codex 26.825.41651 declares ElectronAsarIntegrity for its app.asar package, while Claude 1.26832.0 ships both Electron Framework.framework y app.asar.

Electron’s own documentation also names Visual Studio Code, Figma, Docker Desktop, Loom, Canva, Notion, 1Password, Slack, Discord, and Signal among the products built with the framework. They cover code editing and debugging, collaborative graphics, container management, screen recording, credential management, messaging, voice and video, and AI workflows.

That list is not a performance benchmark, and it does not prove that every Electron app is efficient. It demonstrates the architectural point: choosing Electron for a desktop shell does not require the shell to perform every expensive operation. Native modules, GPU processes, local services, and remote systems can own the specialized work.

You can reproduce the macOS bundle check without special tooling. In Finder, choose Show Package Contents, then inspect Contents/Info.plist, Contents/Resources/app.asar, y Contents/Frameworks/.

Tight Studio uses the second model. On macOS, screen frames are captured by a Swift module using ScreenCaptureKit and written with AVAssetWriter. The recording does not detour through the DOM, React, or a Chromium canvas. During editing and export, GPU compositing and hardware codec APIs do the expensive work; JavaScript coordinates the pipeline.

Apple describes ScreenCaptureKit as its high-performance API for frame and audio capture. Electron does not prevent an app from calling it. A native module can call the same framework, receive the same CMSampleBuffer objects, and hand them to the same system encoder.

What we benchmarked

We avoided comparing two finished products. One app might render captions, cursor motion, a camera cutout, and five overlays while another remuxes the original recording. The faster result would tell us almost nothing about Swift or Electron.

Instead, we held the workload constant:

  • Machine: Apple M4 Mac mini, 10-core CPU, 10-core GPU, 16 GB memory, macOS 26.5.2.
  • Grabación: the exact same packaged native capture engine, run once from its direct Swift CLI and once from Tight Studio’s signed Electron/Node executable. Both recorded the same animated 3840×2160 display at a 60 fps target.
  • Editing: the same procedural background, rounded card, grid, shadow, and animated cursor shader at 3840×2160. Native used Metal; Electron used WebGL 2 through ANGLE’s Metal renderer. Every frame had a synchronization boundary so a hidden canvas could not make the work disappear.
  • Export: 300 frames at 1920×1080, 30 fps, H.264, and a 12 Mbps target. Native used Metal plus VideoToolbox. Electron used WebGL plus WebCodecs with hardware acceleration requested.
  • Runs: three per path. The table reports medians.

No battery result is included. This was a Mac mini, and a credible battery claim needs a longer, controlled MacBook test.

Gráficos de referencia con eje desde cero que comparan grabación nativa directa y Electron híbrido, edición 4K y rendimiento de exportación H.264

Results

PhaseDirect nativeElectron/hybridElectron difference
4K recording output rate57.70 fps57.53 fps-0.29%
Recording host CPU7.06%6.98%-0.09 points
Recording OS-service CPU31.34%32.76%+1.42 points
Recording host peak memory45.36 MB94.39 MB+49.03 MB
4K edit shader throughput684.23 fps593.77 fps-13.22%
4K edit shader p95 frame time1.62 ms1.90 ms+0.28 ms
1080p export throughput226.10 fps223.78 fps-1.02%
1080p export speed7.54x real time7.46x real time-1.02%

These are short, controlled tests on one machine. They establish what is possible with these architectures; they are not a promise that every Electron app will be this efficient.

Recording: Electron does not need to touch a frame

The recording result is almost a tie because both paths are the same where it matters.

ScreenCaptureKit produces the frames. AVAssetWriter and the system codec stack encode them. The Electron side asks the native recorder to start, stop, pause, and resume. It does not receive 4K pixel buffers over an IPC channel and it does not encode them in JavaScript.

Across the three runs, native delivered a median 57.70 fps and the Electron host delivered 57.53 fps—a 0.29% difference. Median recorder CPU was 7.06% for native and 6.98% for the Electron host. The macOS capture services averaged 31.34% and 32.76% respectively.

The frame-rate result is the important one. The tiny CPU reversal is ordinary run-to-run noise, not evidence that Electron somehow accelerates ScreenCaptureKit.

There is a testing wrinkle worth disclosing. macOS grants Screen Recording permission to a signed application identity. Our development Electron binary did not have that permission, so the Electron recording run used Tight Studio’s signed executable in Electron’s Node-compatible mode. That isolates the native bridge but omits a Chromium renderer. We measured the packaged shell separately rather than quietly presenting the 94 MB host as a full Electron app.

Editing: translated Metal is still Metal

JavaScript does not expose Apple’s Metal API directly. That fact is sometimes stretched into a much broader claim: that Electron cannot use Metal-backed GPU rendering.

On macOS, ANGLE provides a Metal backend for OpenGL ES, which is what Chromium can use beneath WebGL. Our Electron benchmark identified its renderer as ANGLE Metal Renderer: Apple M4.

Direct Metal was faster. The median native run rendered 684 frames per second; Electron rendered 594, a 13% throughput gap. P95 frame time increased from 1.62 to 1.90 ms.

That is measurable overhead. It is also 0.28 ms on a frame budget of 16.67 ms at 60 fps.

Real editors do more than one shader, of course. They decode video, upload or import textures, render text, calculate animation state, and respond to input. A careless implementation can burn the remaining budget with copies, allocations, synchronous IPC, or React work. But the browser compositor is not automatically a software renderer, and WebGL on a Mac is not proof that the GPU has been bypassed.

When a WebGL or WebGPU path is not enough, Electron still permits a native Metal module. The architecture can choose its escape hatch per subsystem.

Export: the hardware encoder does not care which button started it

VideoToolbox is Apple’s low-level interface to hardware encoders and decoders. Chromium has a macOS VideoToolbox encoder backend, and WebCodecs exposes a hardware-acceleration preference to applications.

In our native run, VideoToolbox confirmed hardware encoding. The Electron run requested hardware through WebCodecs. WebCodecs does not expose the selected encoder name, so we cannot claim API-level proof of the Electron selection. Its performance and output are consistent with the hardware path: native encoded 300 frames in a median 1.327 seconds; Electron took 1.341 seconds. The outputs differed in size by less than 1%.

That works out to 7.54x versus 7.46x real time—a 1% gap.

This does not mean every Electron exporter is fast. Export slows down when an app:

  • reads pixels back from the GPU for every frame;
  • copies full frames through JavaScript or IPC;
  • encodes in software;
  • serializes decode, render, audio, and encode stages unnecessarily;
  • retains the whole output in memory instead of streaming it;
  • uses the UI thread as the render worker.

Those are pipeline choices, not requirements imposed by Electron.

A real Tight Studio export

Microbenchmarks show where overhead enters. A product test tells us whether the complete pipeline is useful.

We also ran Tight Studio’s existing five-clip export regression fixture three times. The 112-second timeline exercises per-clip decode, GPU rendering, audio, encoding, concatenation, and muxing at 1440×1080. Export times were 81.54, 80.94, and 81.28 seconds. The median was 81.28 seconds, or 1.38x real time.

The generated file was 112.36 seconds long, about 30 fps, with H.264 video and AAC audio. This fixture is not a universal export-speed score—the source media and effects matter—but it is an end-to-end Electron application exporting faster than playback in a repeatable repository test.

Chart showing a 112-second Tight Studio project exporting in a median 81 seconds, or 1.38 times real time

The part Electron really does lose: memory

Electron embeds a multiprocess browser runtime. That costs memory.

The direct native recording host peaked at 45 MB RSS. The signed Electron/Node host peaked at 94 MB. Our minimal Electron editing process tree was around 360 MB, and the export process tree was around 394 MB. A packaged Tight Studio session with a fresh profile settled around 744–749 MB in this particular app state.

Gráfico de memoria con eje desde cero que compara hosts de grabación nativo y Electron equivalentes, con el contexto separado del árbol de procesos de Electron

That last number is not a law of Electron, and it is not a native-app comparison. It is a reminder not to use a minimal test shell as marketing camouflage.

Memory can matter on constrained machines. It can create pressure, increase swap, and indirectly cost energy. But allocated RAM is not the same metric as recording CPU, dropped frames, GPU frame time, or encoder throughput. Treating one as a proxy for all the others produces bad engineering conclusions.

The practical goal is not “use no Chromium memory.” It is to keep the browser runtime away from raw-frame hot paths, close media objects promptly, bound buffers, stream output, and keep idle work idle.

A better way to evaluate a screen recorder

Ask what each subsystem actually does:

  • Which API captures the screen?
  • Where are frames composited?
  • Is the GPU backend hardware accelerated?
  • Which encoder implementation is selected?
  • How many full-frame copies happen?
  • Does pixel data cross IPC or JavaScript boundaries?
  • Can the editor hold 60 fps at the target resolution?
  • What is export time per second of output?
  • What are CPU, memory, dropped-frame, power, and thermal results on the same workload?

“Native” and “Electron” are useful implementation details. They are weak benchmark results.

The conclusion

A fully native app has the lowest possible shell overhead and the most direct access to platform APIs. If two teams build equally optimized, feature-identical pipelines, native retains an advantage—especially in memory and in the last increments of GPU control.

But an Electron app is not required to build its media engine out of DOM nodes and JavaScript loops. It can capture through ScreenCaptureKit, composite on Metal-backed GPU APIs, encode with the hardware media engine, and move performance-critical work into native modules or workers.

Our measurements found a 0.29% recording-frame-rate gap, a 13% synthetic 4K shader-throughput gap, and a 1% controlled export gap. We also found a large memory gap. That is the honest shape of the trade-off.

The framework sets defaults. The pipeline sets performance.

Sources and reproduction

The benchmark harness, raw run table, permission caveat, and interpretation limits are being prepared for a standalone public release. We will add the repository link here when the package can be cloned and run independently. Additional Apple silicon machines and a controlled MacBook power test would broaden these results, but they are not included in the claims above.

compartir en x