Skip to content

Conversation

cloudmark
Copy link
Contributor

Reason for Change

Problem:
When clicking on a TimeSeries visualization, seek events emit inaccurate time values that don't match the clicked data point due to floating-point precision errors in pixel-to-time conversion calculations.

For example:

  • Clicking on data point 1770.677344 emits seek event 1770.6719467913167
  • Clicking on data point 1733.719 emits seek event 1733.72516

These incorrect values don't exist in the original dataset, causing:

  • Synchronization issues with audio/video players
  • Annotation accuracy problems in time-based labeling tasks
  • Confusion between displayed values and actual seek positions

Root Cause:
The pixel-to-time conversion uses continuous interpolation with multiple floating-point operations that accumulate rounding errors:

const timeClicked = brushTimeStartNative + (clickX / plottingAreaWidth) * brushDurationNative;

Solution:
Implemented data point snapping using binary search (O(log n)) to find and return the nearest actual data point from the dataset instead of using interpolated values. Created a shared snapToNearestDataPoint() helper function to eliminate code duplication across three locations.


Videos


Rollout Strategy

No special rollout needed - This is a bug fix with backward compatibility:

  • No feature flags required
  • No environment variables needed
  • No API changes
  • Existing TimeSeries functionality is preserved and improved
  • Change applies automatically to all TimeSeries visualizations

Testing

Manual Testing Performed:

  1. Basic Click Test

    • Clicked on various data points in TimeSeries visualization
    • Verified seek event values now match exact data point values (see fix demo video)
    • Tested with both numeric and date-formatted time columns
  2. Edge Cases

    • Clicks at the very start/end of timeseries
    • Clicks between two closely-spaced data points
    • Sparse data (few points)
    • Dense data (many points)
  3. Synchronization Test

    • TimeSeries synced with Audio using sync attribute
    • Verified audio seeks to exact time when clicking TimeSeries
    • No drift or offset between components
  4. MultiChannel Test

    • Tested with MultiChannel TimeSeries visualization
    • Verified plotClickHandler works correctly with snapping

Code Quality:

  • No linting errors introduced
  • Follows existing code patterns and conventions
  • Well-documented with JSDoc comments

Risks

Risk Level: LOW

Performance:

  • Binary search is O(log n) - efficient even for large datasets
  • No performance degradation compared to original implementation
  • Actually improved by eliminating ~135 lines of duplicated code

Backward Compatibility:

  • No breaking changes
  • Snapping to nearest data point is expected and desired behavior
  • Fallback to original value if data is unavailable
  • All existing TimeSeries functionality preserved

Security:

  • No security implications
  • No new dependencies added
  • No changes to data handling or storage

Reviewer Notes

Key Points to Review:

  1. Helper Function (web/libs/editor/src/tags/object/TimeSeries/helpers.js)

    • New snapToNearestDataPoint() function uses binary search
    • Handles edge cases (null data, empty arrays)
    • Well-documented with JSDoc
  2. Three Refactored Locations (web/libs/editor/src/tags/object/TimeSeries.jsx)

    • emitSeekSync() - Line ~1164
    • plotClickHandler() - Line ~1189
    • handleMainAreaClick() - Line ~1650
    • All now use the shared helper instead of duplicated logic
  3. Code Quality Improvements:

    • Eliminated ~135 lines of duplicated binary search logic
    • Single source of truth for snapping behavior
    • Consistent behavior across all interaction points

Testing the Fix:

  • Watch the fix demonstration video to see the exact values now match
  • Load any TimeSeries task and click on data points
  • Observe that seek events emit exact data point values from the dataset

General Notes

Implementation Details:

The snapToNearestDataPoint() helper function:

  • Takes a calculated time value and the array of actual data points
  • Uses binary search to efficiently find the closest data point
  • Checks adjacent points to ensure absolute closest match
  • Returns exact data point value from the dataset

Files Changed:

  1. web/libs/editor/src/tags/object/TimeSeries/helpers.js - Added shared helper function
  2. web/libs/editor/src/tags/object/TimeSeries.jsx - Refactored three locations to use helper

Benefits:

  • Seek events now emit exact data point values
  • Eliminates floating-point precision errors
  • Improves synchronization accuracy with video/audio
  • Better annotation precision
  • Reduced code duplication
  • Easier to maintain and test

No Migration Required - This is a transparent bug fix that improves accuracy without requiring any user action or configuration changes.


Related Issues

This fix addresses floating-point precision errors that may have affected:

  • TimeSeries synchronization with Audio/Video players
  • Annotation accuracy in time-based labeling tasks
  • TimeSeries region creation precision

@cloudmark cloudmark requested a review from a team as a code owner October 8, 2025 08:18
Copy link

netlify bot commented Oct 8, 2025

👷 Deploy request for heartex-docs pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 2bdc2b9

Copy link

netlify bot commented Oct 8, 2025

👷 Deploy request for label-studio-docs-new-theme pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 2bdc2b9

Copy link

netlify bot commented Oct 8, 2025

Deploy Preview for label-studio-storybook ready!

Name Link
🔨 Latest commit 2bdc2b9
🔍 Latest deploy log https://app.netlify.com/projects/label-studio-storybook/deploys/68f0bde3eeefd600071cc07e
😎 Deploy Preview https://deploy-preview-8602--label-studio-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Copy link

netlify bot commented Oct 8, 2025

Deploy Preview for label-studio-playground ready!

Name Link
🔨 Latest commit 2bdc2b9
🔍 Latest deploy log https://app.netlify.com/projects/label-studio-playground/deploys/68f0bde3485ecd0008918dbd
😎 Deploy Preview https://deploy-preview-8602--label-studio-playground.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

* @param {Array} timeData - Array of time values from the data
* @returns {number} - The snapped time value (or original if no data)
*/
export const snapToNearestDataPoint = (targetTime, timeData) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

please create some unit tests for this as it would make sure that future updates wont break it and to lock in the expected behaviour

@yyassi-heartex
Copy link
Contributor

yyassi-heartex commented Oct 10, 2025

/jira create

Workflow run
Jira issue TRIAG-1675 is created

@yyassi-heartex
Copy link
Contributor

yyassi-heartex commented Oct 10, 2025

/git merge

Workflow run
Successfully merged: create mode 100644 web/libs/core/src/providers/api-provider.tsx

…recision errors

When clicking on a TimeSeries visualization, seek events were emitting
inaccurate time values due to floating-point precision errors in the
pixel-to-time conversion calculations.

For example, clicking on data point 1770.677344 would emit a seek event
with value 1770.6719467913167, which doesn't exist in the original dataset.
This caused synchronization issues with audio/video players and affected
annotation accuracy.

Changes:
- Added snapToNearestDataPoint() helper function in helpers.js using
  efficient binary search (O(log n)) to find the closest actual data point
- Refactored emitSeekSync() to snap center time before emitting
- Refactored plotClickHandler() to snap clicked time before processing
- Refactored handleMainAreaClick() to snap clicked time before processing

Benefits:
- Seek events now emit exact data point values from the dataset
- Eliminates ~135 lines of duplicated code across 3 locations
- Maintains backward compatibility with no breaking changes
- Improves synchronization accuracy with video/audio players
- Ensures annotation precision in time-based labeling tasks

Bug demonstration: https://www.loom.com/share/5f1f429a21f0438ca5f11e7146570bfe
Fix demonstration: https://www.loom.com/share/b1b2b9ea3230461eb6e58848c40edfe2

Files changed:
- web/libs/editor/src/tags/object/TimeSeries/helpers.js
- web/libs/editor/src/tags/object/TimeSeries.jsx

Related To: HumanSignal#8601
@cloudmark cloudmark force-pushed the feature/8601-timeseries-fix branch from a622089 to 7b744fe Compare October 15, 2025 09:51
@yyassi-heartex
Copy link
Contributor

yyassi-heartex commented Oct 16, 2025

/git merge

Workflow run
Successfully merged: create mode 100644 web/libs/core/src/providers/api-provider.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants