Day 3 – February 5, 2026
Date: February 5, 2026
Week: 21
Internship: AI/ML Intern at SynerSense Pvt. Ltd.
Mentor: Praveen Kulkarni Sir
Day 3 – Drag Pathologies, Browser Event Reality & Rendering Stabilization
Primary Goal:
Make dragging correct, predictable, and boring — no flicker, no ghost points, no double clicks, no disappearing plots.
Day 3 was not about adding features.
It was about removing instability.
1. The First Reality Check: “It Works… Until You Touch It”
At the start of Day 3, the drag implementation appeared functional based on artificial test scenarios. Controlled testing environments often mask critical issues because they don’t replicate the chaotic, unpredictable nature of real user interactions. This is a fundamental principle in software engineering: the difference between working in isolation versus working in an integrated system.
Theoretical Context:
In controlled tests, we typically:
- Use perfect, predictable input sequences
- Test one interaction at a time
- Assume ideal network conditions and system states
- Ignore concurrent operations and side effects
However, real-world usage introduces:
- Asynchronous operations: Multiple events firing simultaneously
- State complexity: Interactions between different system components
- User unpredictability: Non-linear interaction patterns
- Performance constraints: CPU, memory, and GPU limitations under load
The symptoms that emerged—blinking points, jumping elements, extra clicks—were not bugs per se, but emergent behaviors arising from the complex interplay between browser event handling, React’s reconciliation algorithm, and the custom rendering pipeline.
This transition from “works in test” to “breaks in reality” is a common pattern in interactive system development, often called the “uncertainty principle” of UI testing: the act of testing changes the behavior being tested.
Key Learning:
Day 3 demonstrated that stability testing requires not just functional verification, but chaos engineering approaches—deliberately introducing real-world conditions to expose hidden coupling and race conditions.
2. Diagnosing the Core Failure Mode: Competing Authorities
The key realization emerged from analyzing the system’s architecture through the lens of distributed state management:
Multiple parts of the system thought they “owned” the visual state.
This is a classic problem in complex UI systems known as authority conflicts or shared mutable state issues. When multiple components believe they have exclusive control over the same resource, race conditions and inconsistent behavior become inevitable.
Theoretical Framework:
The conflicts existed between:
- Canvas Drawing: Imperative rendering that directly manipulates pixels
- Image Snapshot Replacement: Declarative image updates via DOM manipulation
- React Re-renders: Virtual DOM reconciliation that assumes exclusive UI control
- Mouse Event Boundaries: Browser-native event handling that operates outside React’s control
- Ref-based vs React State: The tension between immediate imperative updates and declarative state management
Why This Matters:
Each of these authorities operates on different timelines and with different assumptions:
- Canvas operations are synchronous and immediate
- React updates are batched and asynchronous
- Browser events are unpredictable and high-frequency
- Refs provide direct access but bypass React’s change detection
This created a multi-threaded concurrency problem in a single-threaded environment, where different “threads of execution” (event handlers, render cycles, state updates) were competing for control of the visual output.
Visual Thrashing occurred because:
- A mouse event triggers canvas redraw
- React detects state change and schedules re-render
- Image source updates during re-render
- Another mouse event interrupts the process
- Cycle repeats with inconsistent visual state
Race Conditions emerged from the non-deterministic ordering of these operations, making bugs appear intermittent and hard to reproduce.
The Solution Space:
Resolving authority conflicts requires establishing clear ownership boundaries and communication protocols between different system components, much like designing APIs between microservices.
3. Blob Storms & Why They Were So Dangerous
Console logs revealed something alarming that exposed a fundamental misunderstanding of browser resource management:
generateImageUrl()was being called dozens of times per second- Each call created a new Blob URL
<img>elements were swapping sources continuously
This “Blob storm” was not just a performance issue—it was a resource leak with cascading consequences.
Blob URL Theory:
Blob URLs are browser-managed resources that create a bridge between in-memory data and the DOM. When you call URL.createObjectURL(blob), the browser:
- Stores the blob data in memory
- Creates a unique URL pointing to that data
- Returns the URL for use in
srcattributes
The Critical Problem:
Each generateImageUrl() call created a new Blob URL, but the old ones weren’t being cleaned up. This led to:
- Memory Pressure: Blob data accumulating without garbage collection
- GPU Texture Churn: Each new image source forced GPU texture recreation
- Visible Blinking: DOM updates causing layout recalculations
- GC Spikes: Periodic garbage collection pauses as memory usage grew
Browser Resource Management Context:
Modern browsers implement sophisticated resource management, but they’re not telepathic. The browser assumes that if you create a Blob URL, you intend to use it. Without explicit cleanup via URL.revokeObjectURL(), these resources persist until:
- The document unloads
- Memory pressure forces cleanup
- The browser decides to garbage collect (unpredictably)
Performance Impact:
- Memory: Linear growth with interaction frequency
- GPU: Texture uploads are expensive operations
- CPU: Layout and paint operations triggered by src changes
- User Experience: Visible stuttering during GC pauses
Critical insight:
Snapshot generation must be rare, explicit, and data-driven.
This principle extends beyond this specific bug—it’s a fundamental rule for expensive operations in interactive systems. Expensive operations should be:
- Rare: Triggered by meaningful state changes, not continuous events
- Explicit: Clearly separated from cheap operations
- Data-driven: Tied to actual data mutations, not UI events
The Blob storm exposed how easy it is to accidentally create O(n) resource allocation where O(1) behavior was intended.
4. Removing Snapshot Generation from the Hot Path
One of the most important architectural decisions of the entire project emerged from understanding hot path optimization principles:
generateImageUrl()was removed from redrawStatic()- Static redraws no longer implied snapshot regeneration
- Snapshot creation became a commit-only operation
Hot Path Theory:
In performance-critical systems, the “hot path” refers to the most frequently executed code path. Optimizing the hot path is crucial because:
- Small inefficiencies amplify dramatically with frequency
- Hot path operations determine system responsiveness
- Cold path optimizations have minimal impact
The Original Sin:
By coupling snapshot generation to redrawStatic(), every visual update—hover effects, drag previews, minor animations—triggered expensive blob creation. This violated the separation of concerns between:
- Presentation: What the user sees (cheap, frequent)
- Persistence: Committing changes to stable storage (expensive, rare)
The Corrected Architecture:
Now the system distinguishes between:
- Transient Visual Updates: Canvas drawing for immediate feedback
- Committed State Changes: Image snapshots for stable representation
Performance Impact:
This single change eliminated 80% of the flicker because:
- Hovering became a pure canvas operation
- Drag previews stayed in memory only
- Static visuals stabilized without continuous regeneration
Broader Implications:
This pattern—separating transient UI state from persistent data state—is fundamental to high-performance interactive applications. It prevents expensive operations from being triggered by high-frequency events, ensuring smooth user experience.
Design Principle Established:
Expensive operations belong in commit handlers, not event handlers.
This principle would guide future feature development, ensuring that complex operations like ellipsoid computation or full-width rendering wouldn’t interfere with basic interactions.
5. Mouse Event Boundaries: The “Extra Click to Release” Bug
A subtle but brutal bug appeared that exposed fundamental misunderstandings about browser event propagation and capture:
- Drag release sometimes required clicking twice
- Releasing outside the image didn’t stop the drag
Browser Event Theory:
Mouse events in browsers follow a capture and bubble model:
- Capture Phase: Events travel from window down to target
- Target Phase: Event reaches the target element
- Bubble Phase: Events travel back up to window
The Root Cause:
onMouseUp was attached to the <img> element, but dragging often moves the mouse outside the element boundaries. When mouseup fires outside the element, it doesn’t reach the event listener.
Event Boundary Issues:
- Element Scope: Events are scoped to their target element
- Pointer Drift: During drag, mouse often leaves the original element
- Lost Events:
mouseupevents outside the element are never handled
The Fix:
Binding mouseup to window ensures capture of all mouse release events, regardless of pointer location. This aligns with the principle that drag operations should be global in scope—once dragging starts, the entire window becomes the drag context.
Theoretical Implications:
This fix established an important UI pattern: interaction ownership transcends element boundaries. When a component initiates a drag operation, it assumes responsibility for the entire interaction lifecycle, not just events within its DOM subtree.
Cross-Browser Considerations:
Different browsers handle edge cases differently:
- Some browsers fire events when mouse leaves window
- Others suppress events during drag operations
- Mobile browsers introduce touch event complexities
The Solution Pattern:
For drag interactions:
- Bind
mousedownto the target element (capture start) - Bind
mousemoveandmouseuptowindow(global scope) - Use event coordinates, not element-relative positions
This ensures robust drag behavior across all usage scenarios and browser implementations.
6. The “Disappearing Graph” Incident
A major regression surfaced that exposed the critical distinction between React’s declarative state management and imperative ref-based state:
- On drag start, the entire graph vanished
- On release, it sometimes stayed gone
React State vs Refs Theory:
React’s reconciliation algorithm depends on state changes triggering re-renders. When you update useState, React:
- Compares old and new state
- Calculates DOM changes needed
- Batches updates for performance
- Triggers re-render cycle
Refs, however, are invisible to React’s change detection:
useRefcreates mutable containers outside React’s state system- Changes to
ref.currentdon’t trigger re-renders - Refs are perfect for non-visual state (timers, DOM references)
- But dangerous for state that affects rendering
The Root Cause:
Visibility was tied to dragIndexRef.current !== null, but refs don’t trigger React re-renders. So when dragging started:
dragIndexRef.currentwas set- No re-render occurred
- Visual logic still thought graph should be visible
- But some conditional rendering depended on the ref value
The Corrective Action:
Introduced explicit isDragging React state used only for visibility toggling, while keeping drag logic in refs for performance.
Why This Architecture:
- React State: Handles visual changes and user feedback
- Refs: Manage high-frequency drag coordinates and calculations
- Clear Separation: Each mechanism serves its appropriate purpose
Performance Implications:
This hybrid approach prevents unnecessary re-renders during drag (which would be expensive) while ensuring React maintains control over visual state. It’s a common pattern in high-performance React applications: use refs for imperative logic, state for declarative UI.
The Lesson:
Never use refs for state that affects rendering. React’s state system exists for a reason—bypassing it leads to inconsistent UI behavior and debugging nightmares.
7. The Limbo Point Problem
Another subtle issue emerged that highlighted the dangers of asynchronous state synchronization:
- After release, the dragged point sometimes:
- Was invisible
- Appeared only on hover
- Reappeared after a delay
Timing and Synchronization Theory:
UI systems often involve multiple asynchronous operations that need to stay synchronized:
- React State Updates: Batched and scheduled
- DOM Manipulations: Immediate but potentially reordered
- Event Callbacks: Fired in event loop order
- Rendering: Happens in requestAnimationFrame or setTimeout cycles
The Root Cause:
Snapshot regeneration was deferred via setTimeout(0), creating a race condition:
- React state updates (immediate)
setTimeout(0)schedules snapshot generation- Next event loop tick executes snapshot creation
- Visual state becomes inconsistent during the delay
Why setTimeout(0) Was Used:
Originally intended to:
- Prevent blocking the main thread
- Allow React to finish rendering
- Defer expensive operations
But it introduced temporal coupling—the assumption that operations would complete in a specific order.
The Resolution:
Removed setTimeout entirely. Snapshot regeneration now occurs immediately after commit, ensuring synchronous consistency between data state and visual state.
Theoretical Trade-offs:
- Synchronous: Guarantees consistency but may block UI
- Asynchronous: Prevents blocking but introduces complexity
The Design Decision:
For this system, synchronous snapshot generation was acceptable because:
- Snapshots are relatively fast operations
- User expects immediate visual feedback after drag release
- The alternative (async inconsistency) was worse than brief blocking
Broader Pattern:
This established a principle: expensive but necessary operations should be synchronous if they affect user-visible state. Deferring them creates perception of broken functionality, even if technically correct.
The Key Insight:
In interactive systems, perceived performance (immediate feedback) often matters more than actual performance (throughput). Users tolerate brief pauses but hate inconsistent state.
8. Recognizing the Image-Dragging Trap
A particularly nasty behavior emerged that exposed how browser defaults can hijack custom interactions:
- While dragging a point, the entire image moved
- The browser tried to drag the
<img>itself
Browser Default Behavior Theory:
Browsers have built-in behaviors for common interactions, activated when elements match certain criteria:
- Images: Draggable by default for saving/downloading
- Text: Selectable for copying
- Links: Navigate on click
- Form elements: Submit on enter
These defaults are convenient for standard web pages but destructive for custom applications.
The Image-Dragging Trap:
HTML <img> elements have draggable="true" by default. When users start dragging on an image, the browser assumes they want to drag the image itself, not interact with custom content overlaid on it.
The Conflict:
- Custom Drag: Point manipulation on canvas
- Browser Drag: Moving the entire image element
- Event Hijacking: Browser prevents custom event handlers
Fixes Applied:
draggable={false}: Explicitly disables browser draggingpointer-eventscontrol: Manages which elements receive pointer events- Cursor ownership clarification: Ensures custom cursors override browser defaults
CSS pointer-events Theory:
The pointer-events property controls whether an element can be the target of pointer events:
auto: Element receives events normallynone: Element becomes “transparent” to pointer eventsstroke/fill: Granular control for SVG elements
Strategic Use:
During drag operations, selectively disable pointer events on elements that shouldn’t interfere, ensuring the drag surface has exclusive control.
Browser Compatibility:
Different browsers implement these properties with slight variations:
- Some browsers respect
pointer-events: nonemore strictly - Others have edge cases with nested elements
- Mobile browsers introduce touch-specific behaviors
The Prevention Pattern:
For custom interactive elements:
- Set
draggable={false}on images and other draggable elements - Use
pointer-eventsstrategically during interactions - Test across browsers to ensure no default behaviors interfere
This incident reinforced that building custom interactions requires actively disabling browser defaults, not just adding new behavior.
9. Pointer Events, Capture & Exclusive Control
Day 3 clarified an important UI principle that would inform all future interaction design:
Dragging requires exclusive ownership of the pointer.
Pointer Event Theory:
Modern browsers support the Pointer Events API, which unifies mouse, touch, and pen interactions:
pointerdown: Pointer contact beginspointermove: Pointer moves while in contactpointerup: Pointer contact endspointercancel: Pointer contact cancelled (system intervention)
Advantages over Mouse Events:
- Unified Input: Single API for all pointer types
- Pressure Sensitivity: Access to pen pressure (where supported)
- Multi-touch: Better handling of multiple simultaneous contacts
- Predictable Behavior: Consistent across input methods
Pointer Capture:
The setPointerCapture() method allows an element to “capture” a pointer, ensuring all subsequent events from that pointer are delivered to it, regardless of pointer position. This is crucial for drag operations that extend beyond element boundaries.
Exclusive Control Principle:
Dragging fundamentally changes the user contract:
- Before Drag: Multiple elements compete for input
- During Drag: One element owns the entire interaction
- After Drag: Normal event routing resumes
Implementation Implications:
This led to considering:
- Pointer Events Migration: Moving from mouse events for better cross-device support
- Capture Usage: Using
setPointerCapture()for robust drag handling - Event Leakage Prevention: Ensuring drag operations don’t accidentally trigger other interactions
Interaction Contract Established:
The system now had a clear contract:
- Initiation: Element captures pointer on
pointerdown - Ownership: All pointer events route to the dragging element
- Termination: Capture released on
pointeruporpointercancel - Cleanup: System returns to normal event routing
Future Implications:
This principle would guide the design of:
- Multi-point interactions (future feature)
- Touch gesture support
- Pen input for precision work
- Accessibility considerations for different input methods
The Broader Lesson:
Complex interactions require explicit ownership models. Without clear ownership, event routing becomes unpredictable, leading to the kinds of bugs that plagued Day 3.
10. Stabilization Phase: Making It Boring
By the end of Day 3, the system achieved a state of predictable stability—the holy grail of interactive system development.
What “Boring” Means in UI Development:
“Boring” is the highest compliment for interactive software. It means:
- Predictable: Same input always produces same output
- Reliable: No unexpected behaviors or edge cases
- Stable: No flickering, jumping, or disappearing elements
- Trustworthy: Users can rely on the system behaving consistently
The Transformation Achieved:
- Dragging became predictable: No more random jumps or extra clicks
- Hover stopped blinking: Visual feedback stabilized
- Snapshot updates were controlled: Expensive operations properly gated
- Logs were reduced: Fewer error conditions and edge cases
- Behavior was deterministic: Same actions always produced same results
Theoretical Significance:
This stabilization represented the transition from emergent complexity (unpredictable interactions between components) to composed simplicity (well-understood, predictable behavior).
The Moment of Truth:
“This was the moment the system stopped fighting the user.”
Before stabilization, the system had a hostile relationship with users—every interaction risked exposing another bug. After stabilization, the system became a reliable tool that users could trust.
Quality Assurance Implications:
Stabilization enabled proper testing because:
- Behaviors became reproducible
- Edge cases were eliminated
- Performance became consistent
- User flows became predictable
The Path Forward:
With stability achieved, future development could focus on features rather than fixes. This foundation would support:
- Ellipsoid visualization enhancements
- Full-width responsive design
- Advanced interaction modes
- Performance optimizations
Without this stability, any new feature would have been built on shaky ground, amplifying existing issues rather than adding value.
11. Day 3 Outcome Summary
- ✅ Flicker eliminated
- ✅ Blob storm stopped
- ✅ Drag release fixed globally
- ✅ Snapshot lifecycle stabilized
- ✅ React visibility bugs resolved
- ✅ Browser default drag behavior neutralized
- ✅ Interaction ownership clarified
Why Day 3 Mattered So Much
Day 3 didn’t add features.
It added trust.
The Psychology of Software Stability:
Users develop trust through consistent, predictable experiences. When software behaves reliably, users:
- Learn efficiently: They can build mental models of how the system works
- Work confidently: They don’t hesitate before actions
- Recover gracefully: They know how to fix mistakes
- Recommend enthusiastically: They trust the system with their work
Unstable software creates the opposite:
- Hesitation: Users become afraid to interact fully
- Workarounds: Users develop inefficient patterns to avoid bugs
- Frustration: Users waste time on unexpected behaviors
- Distrust: Users question the system’s reliability
Without Day 3’s stabilization:
- Ellipsoid integration would have amplified chaos: Adding complex 3D visualizations to an unstable drag system would have created impossible debugging scenarios
- Full-width resizing would have broken interactions: Responsive design changes would have exposed new event boundary issues
- The system would feel “janky” forever: Users would perceive the entire application as poorly built, regardless of underlying functionality
The Foundation Principle:
Day 3 established that stability is not optional—it’s the prerequisite for all other work. This is a fundamental truth in software engineering: you can’t build compelling features on a foundation of bugs.
Long-term Impact:
The stabilization work from Day 3 created:
- Development velocity: New features could be added without regression risk
- User adoption: The system became trustworthy enough for real work
- Maintenance ease: Future changes had a stable baseline for comparison
- Architectural clarity: The interaction patterns became well-understood
The Ultimate Outcome:
Day 3 is the reason your plot now feels solid instead of fragile. The smoothness users experience today is built on the debugging battles fought and won on that day. Without Day 3, the system would still be fighting its users instead of empowering them.
The Lesson for Future Projects:
Always allocate time for stabilization. The most important code is often the code that prevents bugs, not the code that adds features. Stability is invisible when present, but painfully obvious when absent.