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:

  1. A mouse event triggers canvas redraw
  2. React detects state change and schedules re-render
  3. Image source updates during re-render
  4. Another mouse event interrupts the process
  5. 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:

  1. Stores the blob data in memory
  2. Creates a unique URL pointing to that data
  3. Returns the URL for use in src attributes

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:

  1. Capture Phase: Events travel from window down to target
  2. Target Phase: Event reaches the target element
  3. 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: mouseup events 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 mousedown to the target element (capture start)
  • Bind mousemove and mouseup to window (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:

  1. Compares old and new state
  2. Calculates DOM changes needed
  3. Batches updates for performance
  4. Triggers re-render cycle

Refs, however, are invisible to React’s change detection:

  • useRef creates mutable containers outside React’s state system
  • Changes to ref.current don’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:

  1. dragIndexRef.current was set
  2. No re-render occurred
  3. Visual logic still thought graph should be visible
  4. 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:

  1. React state updates (immediate)
  2. setTimeout(0) schedules snapshot generation
  3. Next event loop tick executes snapshot creation
  4. 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 dragging
  • pointer-events control: 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 normally
  • none: Element becomes “transparent” to pointer events
  • stroke/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: none more strictly
  • Others have edge cases with nested elements
  • Mobile browsers introduce touch-specific behaviors

The Prevention Pattern:
For custom interactive elements:

  1. Set draggable={false} on images and other draggable elements
  2. Use pointer-events strategically during interactions
  3. 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 begins
  • pointermove: Pointer moves while in contact
  • pointerup: Pointer contact ends
  • pointercancel: 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:

  1. Initiation: Element captures pointer on pointerdown
  2. Ownership: All pointer events route to the dragging element
  3. Termination: Capture released on pointerup or pointercancel
  4. 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.


This site uses Just the Docs, a documentation theme for Jekyll.