GSoC 2026 Wrap-up

Wait 5 sec.

Improving Effect Widgets for KdenliveOrganization: KDE CommunityProject: KdenliveContributor: Yash Bavadiya (@xevrion)Mentor: Jean-Baptiste Mardelle (@mardelle)Reviewers: Julius Künzel (@jlskuz), Bernd Jordan (@bjordan)This is the final post for my Google Summer of Code 2026 project with KDE. Full weekly detail is linked at the bottom; this one's the complete summary.ContentsProject overviewProject statusPre-GSoC workCoding-period deliverables1. Curves widget2. Gradient Map widget3. Speed Ramp widgetAlso merged during GSoCChallengesFuture workAcknowledgementsAll the weekly postsProject overviewKdenlive's effect system exposes powerful underlying libraries (libavfilter, MLT) through custom Qt widgets in the effect panel. Three of those widgets had real, longstanding usability gaps: the Curves effect needed the same filter applied three separate times for independent RGB channel control, the Gradient Map effect only supported two fixed color stops when the underlying MLT filter supports up to 32, and the Time Remapping panel had no way to ease speed changes, every transition was abrupt.The goal of this project was to rebuild all three as proper, well-tested widgets, backed by the correct underlying data formats, with full backward compatibility for existing project files.Project statusThe project is fully complete. All three proposed widgets are merged into Kdenlive's master branch:Curves (!887), merged, shipping in Kdenlive 26.08Gradient Map (!911), merged (JB merged manually after a GitLab auto-merge issue, shows as "Closed" in the MR list but the code landed with commit b0b678c6)Speed Ramp (!928), mergedOne follow-up item remains open: a bug found during Speed Ramp review, where keyframe interpolation types are lost when a clip is resized, was traced to ClipModel::requestRemapResize() in the timeline model, outside this project's original scope. My mentor asked for this as a separate follow-up MR rather than folding it into !928, since it touches unfamiliar timeline code with roughly 15 mutation sites needing updates. That fix is in progress.Pre-GSoC workBefore the coding period began (community bonding ran April 30 to May 24), I landed twelve merged contributions to Kdenlive, mostly while getting familiar with the codebase and building trust with the maintainers:!824: added missing HD-ready (1366×768) project profiles at standard frame rates!827: fixed duplicate profiles appearing in the New Project dialog, root cause was two compounding bugs, MLT shipping spec-duplicate profile files, and refresh() being called twice, breaking naive deduplication!826: added common video/audio/image MIME types to the desktop file, so Kdenlive appears as an option in file manager "open with" menus!831: added configurable clip type colors (video, audio, title, image, slideshow) to the Colors and Markers settings page!835: fixed broken tab order in the Render dialog!837: added an "Identify Gaps" feature to the Timeline menu, detecting empty spans across all video tracks and placing guide markers, went through several rounds of feedback from Eugen Mohr and Bernd Jordan!853: added a warning before deleting tracks that contain clips!857: added tests for guide position preservation across fps changes!858: detect embedded subtitle streams and display the count in clip properties!859: snap the playhead to snap points when dragging in the ruler!874: added a "return playhead to start position on stop" settingwebsites/planet-kde-org!292: added this blog to Planet KDEThis is also where I learned the project's review culture firsthand, small, focused MRs, clear commit messages, and genuine back-and-forth before merge, which set the tone for everything that followed.Coding-period deliverables1. Curves widgetMR !887, closing #2187Replaced a single shared curve control with per-channel tabs (All, R, G, B) for the avfilter.curves effect. Previously, independent per-channel adjustment required applying the effect three separate times.What changed:Each channel is a separate av_curve XML parameter, so the model stores per-channel state directly rather than in a widget-side cache. This went through a real architecture revision: my first version combined all channels into one compound parameter, JB pointed out this would be hard to extend later, and I refactored to separate parameters per his suggestion, closer to how m_mainKeyframeWidget already worked elsewhere in the codebaseFixed a libavfilter segfault triggered by control points placed too close together on the x-axis, replaced an initial reject-the-update guard with point snapping, per Bernd Jordan's suggestion that silently rejecting user input would be confusingRemoved an inherited 5-point limit that only applied to frei0r.curves, not the new avfilter.curves pathAdded optional per-channel colors, a reset button scoped to the active channel, editable input/output spinboxes for the selected point, and a GIMP-style ghost overlay showing other edited channels48 unit tests in tests/avcurvetest.cpp2. Gradient Map widgetMR !911, closing #1064, relating to #2206Replaced a fixed two-color-stop gradient control with a draggable multi-stop editor for the gradientmap MLT filter, which already supported up to 32 stops via stop.N parameters, the UI just never exposed them.What changed, including a real architecture pivot:First version was a fully custom-painted widget, add/remove/drag stops, min 2 max 32, live QLinearGradient previewJulius Künzel suggested looking at the Qt-Color-Widgets library for design inspiration, with an eye toward eventual KDE Frameworks upstreaming. I initially wired in the actual vendored library as a dependencyJB and Julius reviewed that approach and decided against it, Kdenlive shouldn't depend on another external library. The direction became: reimplement the specific behaviors as original code, using the library only as a UX reference, not a dependencyReverted the vendor linkage, restored the original from-scratch widget, and added the missing pieces (alpha-channel checkerboard preview, native QStyle frame styling) as new, independently-written codeDuring review, JB found the gradient rendering as a flat, empty rectangle under Breeze's style, root cause was draw order, the native frame primitive was painting its interior background after the gradient fill. Fixed by drawing the frame first, then the gradient inside its content rectAdded a model-layer 32-stop cap (independent of the widget-level cap, so a hand-edited project file can't bypass it), fixed a barely-visible handle on dark stops, and added an RGBA hover tooltipA known limitation surfaced late in review: MLT's gradientmap filter doesn't actually support alpha in its gradients, an upstream MLT issue, not a bug in this widget. JB merged anyway to make the 26.08 window, tracked as a pre-26.08.0 fix8 unit tests, expanded to full widget-interaction coverage, 27 assertions total3. Speed Ramp widgetMR !928, relating to #2188 and #1454Added per-keyframe interpolation types to the Time Remapping panel, so speed changes can ease in and out instead of switching abruptly at every keyframe boundary, plus a curve band showing the actual interpolated shape.What changed, including the biggest scope revision of the summer:The original proposal described free-draggable bezier handles on the remap timeline. Investigation before writing any code found that MLT has no bezier keyframe type to serialize a time map into, only preset easing typesChecked with JB, free bezier handles in MLT had already been investigated years earlier and found difficult to integrate. Direction became: reuse Kdenlive's existing keyframe type system (the same one used for volume, brightness, and other effects) instead of inventing something newFound KeyframeCurveEditor, an existing widget that samples MLT's true interpolated value per pixel and draws it in a plain QWidget, as the reference patternVerified empirically, with a standalone C test program against the real MLT build, that non-linear keyframe types are genuinely honored during playback, not just valid syntax, and found a real gotcha: the type suffix must be on the segment's starting keyframe, not the ending one, or it's silently treated as linearImplemented per-keyframe type storage, MLT-native serialization via anim_set/serialize_cut, a per-pixel sampled curve, and a type selector, four clean commitsJulius and Bernd raised real usability concerns after testing: the UI mixes time remapping (position-based) and speed keyframing (rate-based) as mental models, without making the distinction clear. JB proposed a longer-term fix, splitting Time Remap and Speed Ramp into two distinct UI modes using MLT's speed_map parameter. Scoped this with JB directly: implemented the two smaller, well-defined pieces immediately (expanding the type list from a curated 4 to the full 13, moving the selector into the toolbar), and opened issue #2231 to track the larger mode split as a post-GSoC follow-upWhile implementing the type-list expansion, found and corrected an assumption from the earlier review discussion: tested directly against MLT and confirmed the old pre-selector behavior serialized as linear, not "discrete" as had been suggested, Discrete is a genuinely different freeze-then-jump behaviorJB later reported a resize bug, keyframe types lost when a clip is resized. Traced the root cause through two incorrect hypotheses (each one investigated with real evidence and honestly ruled out) before finding it: ClipModel::requestRemapResize(), timeline-side code outside this MR's original files, flattens the time_map string during resize without preserving type suffixes. Scoped as a separate follow-up MR per JB's requestAlso merged during GSoC!878: added a "Duplicate Clip" action to the timeline (Ctrl+D, right-click menu), addressing a long-standing feature request (bug 435319). Not one of the three proposal widgets, but real merged work from the same period. During review, JB found and removed two stray method declarations left over from a rebase error, a good reminder to double check rebase output carefully.ChallengesThe recurring theme this summer was discovering, partway through implementation, that an assumption baked into the original plan didn't hold, and needing to change direction with a mentor rather than push forward regardless.The Gradient widget's dependency pivot is the clearest example: a reviewer's suggestion to look at an external library led to actually wiring it in, before the maintainers weighed the tradeoffs and decided against the dependency entirely. The Speed Ramp widget had a similar moment at a lower level, discovering MLT genuinely couldn't support the originally proposed bezier-handle design, verified with a real test program rather than assumed.The most useful habit that came out of this was treating claims, my own included, as things to verify rather than trust. The Speed Ramp resize bug took three separate investigation passes to actually locate, the first two hypotheses were reasonable but wrong, and each was only ruled out by tracing the actual call sites and reproducing the bug directly rather than reasoning about it abstractly. The bug was eventually found in code outside the original three files this project touched.Future workrequestRemapResize fix: the resize bug described above, scoped as its own MR, in progressTime Remap / Speed Ramp mode split: tracked in #2231, a larger UI rework splitting position-based and rate-based remapping into two distinct modes, deferred by mutual agreement with JB given the time remainingMLT gradientmap alpha support: the upstream MLT limitation flagged during Gradient widget review, needs a fix or an explicit UI restriction before Kdenlive 26.08.0 finalNone of these block the core functionality already merged. All three proposed widgets are in master and working.AcknowledgementsThanks to Jean-Baptiste Mardelle, my mentor, for consistently thorough review across all three widgets and for being willing to change direction, twice, on real architectural questions, when new information came up, rather than defending an earlier decision for its own sake. Thanks to Julius Künzel for the UX instincts that reshaped the Gradient and Speed Ramp widgets for the better, and to Bernd Jordan for close, careful review on every MR and for sketching out concrete alternatives when something wasn't working. Genuinely a great summer.All the weekly postsGoals for GSoC 2026Week 1: Coding BeginsWeek 1: Tabs are inWeek 2: Refactor and ReviewWeek 3: Reviewer Feedback and FixesWeek 4: Channel Colors, Reset, Point Values, and Ghost CurvesWeek 5: Curves Merged, Gradient Widget BeginsWeek 6: Gradient Widget Wired to Qt-Color-WidgetsWeek 7: Gradient Widget Review FixesWeek 8: Gradient Widget Merged, Speed Ramp BeginsWeek 9 & 10: Speed Ramp Implementation, MR OpenedWeek 11: Review Feedback, a Correction, and What's DeferredFull status report and week-by-week technical detail also on the KDE Community Wiki.