Skip to content

fix: resolve blurry screen recordings and video editor previews#419

Merged
siddharthvaddem merged 4 commits intosiddharthvaddem:mainfrom
rajtiwariee:fix/video-blur
Apr 25, 2026
Merged

fix: resolve blurry screen recordings and video editor previews#419
siddharthvaddem merged 4 commits intosiddharthvaddem:mainfrom
rajtiwariee:fix/video-blur

Conversation

@rajtiwariee
Copy link
Copy Markdown
Contributor

@rajtiwariee rajtiwariee commented Apr 11, 2026

Pull Request Template

Description

This PR resolves two related bugs causing blurry video output during both real-time capture and playback inside the editor. It forces the MediaRecorder to prioritize hardware-accelerated codecs (H.264) and removes static visual filters in PixiJS that were destroying canvas resolution via off-screen render texture downscaling.

Motivation

Users on high-resolution displays (like M-series Macs) were experiencing severe recording lag/blurriness due to AV1 software encoding bottlenecking. Furthermore, even perfectly captured videos appeared pixelated and blurry when scaled down to fit the window during editor playback, rendering screen recordings of code or text illegible.

Type of Change

  • New Feature
  • Bug Fix
  • Refactor / Code Cleanup
  • Documentation Update
  • Other (please specify)

Related Issue(s)

#381 (comment)

Testing

  1. Launch the app and take a new screen recording on a high-resolution display (e.g., Mac 4k natively).
  2. Note that the output in the terminal logs will show encoding via video/webm;codecs=h264 ensuring 60fps captures without dropped frames.
  3. Once the Editor preview window launches, verify that the text inside the bounds of the video player remains razor-sharp when scaling the editor window size up and down (assuming the "Blur BG" toggle and "Motion Blur" sliders are off).

Summary by CodeRabbit

  • Bug Fixes

    • Motion blur now updates dynamically during video playback for smoother visuals and correct enable/disable behavior.
    • Screen recording codec preference order adjusted to improve compatibility and recording reliability across devices and browsers.
  • Chores

    • Build/config tweaks applied with no changes to user-visible features.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4a2263f5-b169-4adf-8768-57e9cc8495e8

📥 Commits

Reviewing files that changed from the base of the PR and between a26eb3c and 8e1c7e0.

📒 Files selected for processing (1)
  • src/components/video-editor/VideoPlayback.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/video-editor/VideoPlayback.tsx

📝 Walkthrough

Walkthrough

Motion-blur is toggled at runtime inside the Pixi ticker in the video playback component; screen-recorder mime/codec preference now prefers h264 before vp8/vp9/av1. The rest of the diff is reordered named exports across UI components and small build/config include and comment tweaks.

Changes

Cohort / File(s) Summary
Video playback filter runtime
src/components/video-editor/VideoPlayback.tsx
Motion blur is evaluated each Pixi ticker tick using motionBlurAmount and playback state; when the active flag flips the container's filters is set to [blurFilter, motionBlurFilter] or null; cleanup now sets videoContainer.filters = null.
Screen recorder codec ordering
src/hooks/useScreenRecorder.ts
selectMimeType() codec preference order changed to prefer video/webm;codecs=h264, then vp8, vp9, and av1; selection still uses find + MediaRecorder.isTypeSupported with a video/webm fallback.
UI export reorderings
src/components/ui/accordion.tsx, src/components/ui/card.tsx, src/components/ui/dialog.tsx, src/components/ui/dropdown-menu.tsx, src/components/ui/popover.tsx, src/components/ui/select.tsx, src/components/ui/tabs.tsx, src/components/ui/tooltip.tsx
Only the order of named exports in each module's export { ... } block changed; no implementations, types, or signatures modified.
TypeScript & build config
tsconfig.node.json, vite.config.ts
tsconfig.node.json include list extended to add several config files; vite.config.ts had inline comment removals and a small ternary refactor for the Electron renderer option.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • siddharthvaddem

Poem

motion blur wakes each tick, then sleeps again,
codecs shuffle lines — h264 takes the train,
exports rearranged like furniture at sea,
tiny config edits, lowkey risky, kinda cursed-y,
midnight patchwork hums — smooth enough to grin ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately captures the two main fixes: resolving blurry screen recordings and video editor previews via codec prioritization and filter removal.
Description check ✅ Passed The PR description covers all key template sections: clear problem statement, motivation explaining the root causes, correct bug fix classification, related issue link, and detailed testing steps with expected outcomes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7774a78898

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/components/video-editor/VideoPlayback.tsx Outdated
Comment thread electron-builder.json5 Outdated
Comment thread electron-builder.json5 Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/video-editor/VideoPlayback.tsx (1)

67-103: ⚠️ Potential issue | 🔴 Critical

critical: blur props removed but VideoEditor still passes them

VideoPlayback no longer accepts blurRegions, selectedBlurId, onSelectBlur, onBlurPositionChange, onBlurSizeChange, onBlurDataChange, or onBlurDataCommit—but VideoEditor.tsx is still passing all of these at lines 1811-1817, 1876, 1880-1881, and 1982-1983. this will cause TypeScript errors.

either add these props back to VideoPlayback (if blur handling should stay there) or remove all these prop passes from VideoEditor. looks like blur got moved elsewhere though, so you probably want to just strip them all out of the VideoEditor call sites.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/video-editor/VideoPlayback.tsx` around lines 67 - 103, The
VideoPlayback component signature no longer includes blur-related props
(blurRegions, selectedBlurId, onSelectBlur, onBlurPositionChange,
onBlurSizeChange, onBlurDataChange, onBlurDataCommit), but VideoEditor is still
passing them into the <VideoPlayback ... /> call; remove all these blur prop
passes from the VideoPlayback invocations in VideoEditor (or alternatively
restore the props on VideoPlayback if blur handling should remain there). Locate
the <VideoPlayback> usage in VideoEditor and delete each of the following prop
assignments: blurRegions, selectedBlurId, onSelectBlur, onBlurPositionChange,
onBlurSizeChange, onBlurDataChange, onBlurDataCommit so the props match the
VideoPlaybackProps interface.
🧹 Nitpick comments (1)
electron-builder.json5 (1)

1-39: this config overhaul seems unrelated to the PR objective

the PR description mentions fixing blurry recordings via codec changes (H.264 preference) and PixiJS filter removal. this electron-builder rewrite is... kinda tangential? removing wallpaper resources, changing build targets, and updating appId don't really connect to video blur fixes.

might be worth splitting this into a separate PR to keep the blur fix atomic and reviewable. bundling unrelated config changes makes it harder to bisect if something breaks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@electron-builder.json5` around lines 1 - 39, This PR mixes unrelated
packaging changes with the blur-fix; revert the unrelated edits in the current
change and move them to a separate PR: restore the original values for
electron-builder config keys modified here (appId, productName,
directories.output/buildResources, files list, mac.identity,
mac.artifactName/mac.target and arch, mac.category, linux.target/linux.category,
win.target/arch) so this PR only includes the H.264 preference and PixiJS filter
removal, then create a new PR that contains the full electron-builder.json5
overhaul (the packaging changes) for independent review and testing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@electron-builder.json5`:
- Around line 13-23: The mac build config currently uses "target": "dir" with
"arch": ["arm64"] and "identity": null which produces only an unpacked app for
arm64 and disables code signing; change the "mac" config to include proper
distributable targets (e.g., "dmg" and/or "zip" in the "target" array instead of
or in addition to "dir"), broaden "arch" to include "x64" or use "universal" so
Intel Macs are supported, and remove or replace "identity": null with the
correct signing identity or configuration for CI signing (or add a conditional
that keeps null only for local testing). Ensure artifactName remains appropriate
for the chosen targets.
- Line 3: The appId value ("appId") was changed from
com.siddharthvaddem.openscreen to com.openscreen.app which will cause OSs to
treat installs as a new app (losing preferences, app data, and permissions);
either revert the change in electron-builder.json5 to the original appId to
preserve continuity or, if this rebrand is intentional, update the
release/migration plan and repository docs to include steps for migrating user
data and re-requesting platform permissions (macOS screen-recording entitlement,
Windows AppUserModelID, etc.) and ensure all build configs and entitlements use
the new appId consistently.
- Around line 9-12: The packaged app no longer includes wallpapers at
process.resourcesPath/assets because extraResources was removed from
electron-builder.json5 and Vite now places public/wallpapers into
dist/wallpapers, causing electron/ipc/handlers.ts (which expects
process.resourcesPath/assets/) to miss them; fix by either restoring
extraResources with an assets mapping so wallpapers are copied to
process.resourcesPath/assets, or update the handler in electron/ipc/handlers.ts
to check process.resourcesPath/dist/wallpapers (or both locations) and/or change
the Vite build output so wallpapers land at the expected assets path; update
only the relevant config (electron-builder.json5 "extraResources") or the
handler path resolution logic to normalize and look for wallpapers under
dist/wallpapers when packaged.

In `@src/components/video-editor/VideoPlayback.tsx`:
- Around line 1015-1026: The conditional that applies Pixi filters wrongly
includes showBlurRef, causing CSS background blur to trigger Pixi's offscreen
RenderTexture; change the logic in the block that uses videoContainerRef,
blurFilterRef, motionBlurFilterRef so only motion blur controls Pixi filters
(i.e., use isMotionBlurActive/motionBlurAmountRef and isPlayingRef) and remove
showBlurRef from the condition; ensure that when motion blur is inactive you
clear videoContainerRef.filters (set to null) so Pixi does not intercept native
rendering, or if there is a separate intended video blur effect add a distinct
flag/function (e.g., videoBlurActive) and gate Pixi filter application on that
instead of showBlurRef.
- Around line 1331-1332: The overlay's clientWidth/clientHeight are read
directly during render (containerWidth={overlayRef.current?.clientWidth || 800}
/ containerHeight={overlayRef.current?.clientHeight || 600}), so resizes don't
trigger React re-renders and annotations can be stale; fix by tracking overlay
dimensions in state (e.g., overlayWidth, overlayHeight) and updating them inside
the existing ResizeObserver callback (the one that calls layoutVideoContent())
or a new resize handler, then pass those state values to the annotation overlay
instead of reading overlayRef.current directly; update any references in
VideoPlayback (overlayRef, layoutVideoContent, and the annotation container
props) to use the state so pure resizes force a re-render.

---

Outside diff comments:
In `@src/components/video-editor/VideoPlayback.tsx`:
- Around line 67-103: The VideoPlayback component signature no longer includes
blur-related props (blurRegions, selectedBlurId, onSelectBlur,
onBlurPositionChange, onBlurSizeChange, onBlurDataChange, onBlurDataCommit), but
VideoEditor is still passing them into the <VideoPlayback ... /> call; remove
all these blur prop passes from the VideoPlayback invocations in VideoEditor (or
alternatively restore the props on VideoPlayback if blur handling should remain
there). Locate the <VideoPlayback> usage in VideoEditor and delete each of the
following prop assignments: blurRegions, selectedBlurId, onSelectBlur,
onBlurPositionChange, onBlurSizeChange, onBlurDataChange, onBlurDataCommit so
the props match the VideoPlaybackProps interface.

---

Nitpick comments:
In `@electron-builder.json5`:
- Around line 1-39: This PR mixes unrelated packaging changes with the blur-fix;
revert the unrelated edits in the current change and move them to a separate PR:
restore the original values for electron-builder config keys modified here
(appId, productName, directories.output/buildResources, files list,
mac.identity, mac.artifactName/mac.target and arch, mac.category,
linux.target/linux.category, win.target/arch) so this PR only includes the H.264
preference and PixiJS filter removal, then create a new PR that contains the
full electron-builder.json5 overhaul (the packaging changes) for independent
review and testing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c8438d75-24a0-45aa-a8b0-63a61063d6fc

📥 Commits

Reviewing files that changed from the base of the PR and between 68295b2 and 7774a78.

📒 Files selected for processing (3)
  • electron-builder.json5
  • src/components/video-editor/VideoPlayback.tsx
  • src/hooks/useScreenRecorder.ts

Comment thread electron-builder.json5 Outdated
Comment thread electron-builder.json5 Outdated
Comment thread electron-builder.json5 Outdated
Comment thread src/components/video-editor/VideoPlayback.tsx
Comment thread src/components/video-editor/VideoPlayback.tsx Outdated
@marcgabe15
Copy link
Copy Markdown
Collaborator

Reviewed, this fixes the issue on my Mac as well where the video editor was blurry after screen recording

@siddharthvaddem
Copy link
Copy Markdown
Owner

@rajtiwariee will merge once the linting is fixed

@rajtiwariee
Copy link
Copy Markdown
Contributor Author

@rajtiwariee will merge once the linting is fixed

Will fix the linting issue

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/components/video-editor/VideoPlayback.tsx (1)

1083-1095: motion blur gating looks good, tiny perf nit

the fix addressed the earlier concern — showBlurRef is out, and pixi filters are now driven purely by isMotionBlurActive. nice, that should stop the offscreen RenderTexture downscaling path from kicking in when only CSS bg blur is on.

one small 2am thought though: this runs every ticker frame and allocates a fresh [blurFilter, motionBlur] array (or reassigns null) on every tick, even when the active state hasn't changed. pixi v8 treats the filters setter as a state change, so you're lowkey churning the filter pipeline ~60x/sec. not a bug, just wasteful. caching the last applied state and only writing on transitions would be cleaner:

♻️ optional: only toggle on state change
+			let lastMotionBlurActive: boolean | null = null;
 			const ticker = () => {
 				// ... existing logic ...

 				const isMotionBlurActive = (motionBlurAmountRef.current || 0) > 0 && isPlayingRef.current;

-				if (
-					isMotionBlurActive &&
-					blurFilterRef.current &&
-					motionBlurFilterRef.current &&
-					videoContainerRef.current
-				) {
-					videoContainerRef.current.filters = [blurFilterRef.current, motionBlurFilterRef.current];
-				} else if (videoContainerRef.current) {
-					videoContainerRef.current.filters = null;
-				}
+				if (isMotionBlurActive !== lastMotionBlurActive && videoContainerRef.current) {
+					if (
+						isMotionBlurActive &&
+						blurFilterRef.current &&
+						motionBlurFilterRef.current
+					) {
+						videoContainerRef.current.filters = [
+							blurFilterRef.current,
+							motionBlurFilterRef.current,
+						];
+					} else {
+						videoContainerRef.current.filters = null;
+					}
+					lastMotionBlurActive = isMotionBlurActive;
+				}
 			};

also heads up — the cleanup at line 859 uses videoContainer.filters = [] while here you're using null. both work in pixi v8 but picking one is kinda nicer for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/video-editor/VideoPlayback.tsx` around lines 1083 - 1095, The
ticker currently reassigns videoContainerRef.current.filters every frame
(allocating a new array or null) even when state hasn't changed; introduce a
cached state (e.g., lastIsMotionBlurActive or lastAppliedFiltersRef) and only
set videoContainerRef.current.filters to [blurFilterRef.current,
motionBlurFilterRef.current] or null when that cached state differs from the
computed isMotionBlurActive to avoid churning Pixi's filter pipeline in the
ticker; also normalize the "empty" cleanup value to match the other usage
(choose either [] or null and use it consistently where videoContainer.filters
is cleared) so cleanup and runtime behavior are consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/components/video-editor/VideoPlayback.tsx`:
- Around line 1083-1095: The ticker currently reassigns
videoContainerRef.current.filters every frame (allocating a new array or null)
even when state hasn't changed; introduce a cached state (e.g.,
lastIsMotionBlurActive or lastAppliedFiltersRef) and only set
videoContainerRef.current.filters to [blurFilterRef.current,
motionBlurFilterRef.current] or null when that cached state differs from the
computed isMotionBlurActive to avoid churning Pixi's filter pipeline in the
ticker; also normalize the "empty" cleanup value to match the other usage
(choose either [] or null and use it consistently where videoContainer.filters
is cleared) so cleanup and runtime behavior are consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7fc4fd8d-0761-4a03-b0c0-d5ff4dbe67f4

📥 Commits

Reviewing files that changed from the base of the PR and between 90d04c7 and f4e10b2.

📒 Files selected for processing (11)
  • src/components/ui/accordion.tsx
  • src/components/ui/card.tsx
  • src/components/ui/dialog.tsx
  • src/components/ui/dropdown-menu.tsx
  • src/components/ui/popover.tsx
  • src/components/ui/select.tsx
  • src/components/ui/tabs.tsx
  • src/components/ui/tooltip.tsx
  • src/components/video-editor/VideoPlayback.tsx
  • tsconfig.node.json
  • vite.config.ts
✅ Files skipped from review due to trivial changes (10)
  • src/components/ui/accordion.tsx
  • src/components/ui/tabs.tsx
  • src/components/ui/card.tsx
  • tsconfig.node.json
  • src/components/ui/tooltip.tsx
  • src/components/ui/dropdown-menu.tsx
  • src/components/ui/select.tsx
  • src/components/ui/dialog.tsx
  • src/components/ui/popover.tsx
  • vite.config.ts

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/video-editor/VideoPlayback.tsx`:
- Around line 1085-1097: The bug is that lastMotionBlurActive is set to
isMotionBlurActive even when filters weren't applied due to missing refs, which
can permanently prevent re-applying them; in the effect surrounding
isMotionBlurActive, only update lastMotionBlurActive after you actually change
videoContainerRef.filters: when entering the "apply" branch, check
blurFilterRef.current and motionBlurFilterRef.current and set filters then set
lastMotionBlurActive = true; in the "clear" branch set filters = null and then
set lastMotionBlurActive = false; alternatively compute the actualApplied =
isMotionBlurActive && blurFilterRef.current && motionBlurFilterRef.current and
assign lastMotionBlurActive = actualApplied so the cached flag reflects the real
filter state (references: lastMotionBlurActive, isMotionBlurActive,
blurFilterRef, motionBlurFilterRef, videoContainerRef).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cd93fa14-3e1c-457b-a288-dc73a465f4ae

📥 Commits

Reviewing files that changed from the base of the PR and between f4e10b2 and a26eb3c.

📒 Files selected for processing (1)
  • src/components/video-editor/VideoPlayback.tsx

Comment thread src/components/video-editor/VideoPlayback.tsx
@siddharthvaddem siddharthvaddem merged commit 4921396 into siddharthvaddem:main Apr 25, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants