This application demonstrates the power of the Fluid Framework by building a comprehensive collaborative canvas and data table application. Users can work together in real-time to create, edit, and interact with shapes, sticky notes, tables, ink drawings, visual connections, and comments. The app showcases both persistent data synchronization (SharedTree) and ephemeral real-time collaboration (Presence API) working together to create a rich collaborative experience.
- Shapes: Create and manipulate circles, squares, triangles, and stars with different colors and sizes
- Sticky Notes: Add collaborative text notes that can be edited by multiple users
- Tables: Create data tables with different column types (string, number, boolean, date, vote)
- Ink Drawing: Draw freehand ink strokes with pen, mouse, or touch input
- Visual Connections: Draw directional connections between items (groups, notes, text blocks, and tables) with intelligent pathfinding that routes around obstacles
- Real-time Collaboration: See other users' selections, edits, and presence indicators in real-time
- Drag & Drop: Move items around the canvas with live position updates
- Rotation & Resize: Transform shapes with real-time preview for other users
- Layering: Manage z-order with bring-to-front, send-to-back operations
- Undo/Redo: Full undo/redo system for all canvas operations
- Multi-user Selection: Visual indicators showing which users have selected which items (ephemeral state)
- User-specific Badges: Each user gets a unique color and initials badge for real-time identification
- Collapsible Selection UI: When multiple users select the same item, see a count badge that expands to show individual users
- Real-time Awareness: See where other users are working without cluttering persistent data
- Multiple Column Types: String, number, boolean, date, and voting columns
- Row/Column Operations: Add, delete, move, and reorder table elements
- Cell-level Collaboration: Multiple users can edit different cells simultaneously
- Table Selection: Select and manipulate rows, columns, or individual cells
- Sorting & Filtering: Interactive table operations with real-time sync
- Directional Connections: Create visual connections between canvas items by dragging from connection points
- Supported Items: Groups, sticky notes, text blocks, and tables all support connection points
- Smart Pathfinding: Connections use A* pathfinding algorithm to intelligently route around obstacles
- Proximity-Based UI: Connection points appear when cursor is near an item, with smooth opacity transitions
- Dynamic Routing: Connection lines automatically recalculate when items move or resize
- Visual Feedback: Active connection points highlight during drag operations for clear interaction
- Persistent State: All connections are stored in SharedTree and synchronized across users
- Item Comments: Add threaded comments to any canvas item
- Voting System: Vote on comments and items with real-time vote counts
- User Attribution: All comments and votes are attributed to specific users
This app is designed to use Azure Fluid Relay a Fluid relay service offered by Microsoft. You can also run a local service for development purposes. Instructions on how to set up a Fluid relay are on the Fluid Framework website.
To use AzureClient's local mode, you first need to start a local server.
npm run start:serverRunning this command from your terminal window will launch the Azure Fluid Relay local server. Once the server is started, you can run your application against the local service.
npm run startThis command starts the webpack development server, which will make the application available at http://localhost:8080/.
One important note is that you will need to use a token provider or, purely for testing and development, use the insecure token provider. There are instructions on how to set this up on the Fluid Framework website.
All the code required to set up the Fluid Framework and SharedTree data structure is in the infra folder. Most of this code will be the same for any app.
The application uses a comprehensive SharedTree schema defined in the src/schema/ folder:
appSchema.ts: Main schema definitions including:Shape: Geometric shapes (circle, square, triangle, star) with size, color, and typeNote: Text-based sticky notes with authorshipFluidTable: Collaborative tables with multiple column typesItem: Canvas items that can contain shapes, notes, or tables with directional connections between itemsItems: Simple array containing Item objects for canvas elementsVote: Voting system for comments and itemsComment: Threaded comments with user attributionInkStroke: Freehand drawing strokes with points and stylingDateTime: Date/time values for timestamps
containerSchema.ts: Fluid container configuration
The schema supports rich data types including strings, numbers, booleans, dates, ink strokes, directional connections, and custom voting objects. All schema changes are automatically synchronized across all connected clients.
Connection System:
- Each
Itemmaintains an array of connection IDs representing items that connect TO it - Methods include:
addConnection(),removeConnection(),hasConnection(),getConnections() - Connections are directional (from source item to target item)
- Stored persistently in SharedTree and synchronized across all users
The application includes a comprehensive undo/redo system located in src/undo/:
- Transaction-based: All operations are wrapped in transactions for atomicity
- Cross-user: Undo operations work across multiple collaborators
- Type-safe: Strongly typed undo/redo operations
- Keyboard shortcuts: Ctrl+Z / Ctrl+Y for undo/redo
- UI indicators: Toolbar buttons show undo/redo availability
The application leverages two key Fluid Framework technologies for real-time collaborative data management:
- Persistent Data: All application data (shapes, notes, tables, comments) is stored in SharedTree
- Automatic Synchronization: Data changes are automatically synchronized across all clients
- Transactional Operations: Uses
Tree.runTransaction()to ensure data consistency - Tree Events: UI updates are driven by tree change events for optimal performance
- Type Safety: Strongly-typed schema ensures data integrity
The Fluid Framework Presence API is a separate system from SharedTree that handles ephemeral, real-time user interactions. This implementation wraps a sophisticated signal-based state management system into an easy-to-use API with custom helpers for optimal performance and developer experience.
Signal-Based State Management:
- Optimized WebSocket Usage: Manages signals automatically to prevent unintended flooding of the WebSocket endpoint
- Efficient Updates: Only sends necessary state changes, reducing network overhead
- Smart Batching: Groups related updates to minimize communication costs
Automatic State Synchronization:
- New Client Onboarding: When clients join, they automatically receive current presence state without querying persistent data
- Clean Disconnections: When clients leave, their presence state is automatically cleaned up across all remaining clients
- Always Up-to-Date: Every client maintains consistent presence state without relying on SharedTree persistence
- Multi-user Selection: Tracks which users have selected which canvas items or table elements
- Selection Indicators: Visual badges showing user initials and colors for each selection
- Collapsible UI: When multiple users select the same item, shows a count badge that expands to individual user badges
- Drag & Drop Preview: Shows live position updates as users drag items around the canvas
- Rotation Preview: Real-time rotation feedback during shape manipulation
- Resize Operations: Live resize preview with immediate visual feedback
- User Cursors: Track and display where other users are actively working
- User Profiles: Manages user information (names, IDs, avatars)
- Color Assignment: Consistent color coding for each user across all presence indicators
- Connection Status: Tracks which users are currently connected and active
The presence system is implemented through several key components with custom helper utilities for simplified usage:
Core Managers:
PresenceManager<T>: Generic presence manager for different interaction typesSelectionManager<T>: Handles multi-user selection state with signal optimizationDragManager: Manages real-time drag and rotation operationsResizeManager: Handles collaborative shape resizing with live previewUsersManager: Tracks connected users and their profiles
Helper Utilities:
usePresenceManagerHook: React hook that simplifies presence subscriptions and automatic cleanup- Signal Optimization: Built-in batching and debouncing to prevent WebSocket flooding
- State Reconciliation: Automatic handling of client join/leave scenarios
- Type-Safe APIs: Strongly-typed interfaces for all presence operations
This architecture ensures optimal performance while providing a developer-friendly API that abstracts away the complexity of signal management and state synchronization.
Unlike SharedTree data (which persists), presence data is ephemeral and only exists while users are actively connected and interacting.
This application demonstrates a dual-layer architecture using two distinct Fluid Framework systems:
- Purpose: Stores and synchronizes all persistent application data
- Data Types: Shapes, notes, tables, comments, votes, user-generated content
- Persistence: Data survives user disconnections and browser refreshes
- Synchronization: Changes are persisted to the Fluid service and synchronized to all clients
- Use Cases: Creating shapes, editing notes, adding table rows, posting comments
- Purpose: Handles real-time, temporary user interactions and awareness using signal-based state management
- Data Types: Selection state, cursor positions, drag operations, user status
- Persistence: Data is lost when users disconnect (intentionally ephemeral)
- Synchronization: Real-time broadcasts to active clients only, with optimized signal management
- Use Cases: Showing who's selecting what, live drag preview, user avatars/status
Signal-Based Optimizations:
- WebSocket Efficiency: Prevents flooding by managing signal frequency and batching updates
- Automatic State Management: New clients receive current state instantly without database queries
- Clean Resource Management: Disconnected clients are automatically cleaned up from all remaining clients
This separation allows for:
- Optimal Performance: Presence data doesn't clutter persistent storage and signals are optimized for minimal network usage
- Privacy: Temporary interactions aren't permanently recorded
- Scalability: Ephemeral data automatically cleans up when users leave, with efficient signal management
- User Experience: Immediate feedback without waiting for data persistence, with smart batching for smooth interactions
- Developer Efficiency: Simple APIs abstract complex signal management and state synchronization
- CRUD Operations: Create, read, update, and delete operations on all data types
- Undo/Redo: Transaction-based undo/redo functionality
- Bulk Operations: Add multiple table rows, duplicate items
- Data Validation: Type-safe operations with runtime validation
- Virtual Scrolling: Efficient rendering of large tables
- Incremental Updates: Only re-render changed components
- Event Batching: Optimized event handling for smooth collaboration
The application follows Fluid Framework best practices by treating the SharedTree as the single source of truth and using tree events to update the UI reactively.
The canvas uses a hybrid SVG + HTML strategy to balance fidelity, performance, and layout flexibility:
- SVG Root (
Canvas.tsx): Owns the unified coordinate system (pan + zoom transform) and renders:- Persistent ink polylines (vector, efficient at scale)
- Ephemeral ink (local + remote) with reduced opacity
- Selection, presence, and comment overlays positioned in logical space
- Connection overlays with interactive connection points and pathfinding
- Custom cursor / eraser feedback (screen-space overlay)
- HTML Layer (foreignObject): Hosts complex React components (tables, notes, shapes) so they can leverage normal DOM/CSS layout & accessibility while still moving/zooming with the SVG transform.
| Space | Purpose | Example |
|---|---|---|
| Screen | Raw pointer events (clientX/clientY) | 742, 312 |
| Canvas | Screen adjusted by SVG bounding rect | (screen - svgRect) |
| Logical | Pan + zoom invariant content coordinates | (canvas - pan) / zoom |
All ink points are stored in logical space so panning/zooming does not degrade precision.
Implemented in useCanvasNavigation:
- Pan: Right-mouse drag (or programmatic) updates a pan vector applied via
transform="translate(x,y) scale(z)"on root groups. - Zoom: Discrete zoom steps (predefined array) selected by wheel gestures; position under cursor is preserved (anchor zoom) by adjusting pan.
- Bounding Context: Pattern background uses inverse scaling for dot size consistency.
- Pointer down (ink mode) starts an ephemeral stroke (presence broadcast only).
- Pointer moves add points (with adaptive thinning: denser for touch). Updates are throttled with
requestAnimationFrameto avoid WebSocket flooding. - Pointer up commits the stroke as a persistent
InkStroke(schema object) with a computed bounding box; presence is cleared. - Remote ephemeral strokes render semi-transparently until those users commit.
- Circular hit test in logical space combines stroke half-width + eraser radius.
- First intersecting stroke is deleted (future enhancement: partial splitting + adjustable radius).
- Coalesced pointer events (when supported) for high‑resolution pen/touch without overwhelming React.
- VectorEffect
non-scaling-strokepreserves stroke aesthetics under zoom while hit tests still use logical widths. - Presence overlays keyed by selection/motion hashes to avoid unnecessary re-renders.
- Bounding box pre-filter before per-segment intersection math.
Ephemeral ink presence is defined by InkPresenceManager (see src/presence/Interfaces/InkManager.ts):
| Method | Description |
|---|---|
setStroke |
Begin a new local ephemeral stroke |
updateStroke |
Replace cumulative point list during draw |
clearStroke |
End/abort local stroke and notify others |
getRemoteStrokes |
Snapshot of all active remote strokes |
Design rationale:
- Keeps transient drawing state out of SharedTree (lower churn + faster feedback).
- One active stroke per attendee simplifies rendering (direct mapping to a polyline).
- Cumulative point updates allow late joiners to render the full in-progress stroke instantly.
The application features a sophisticated visual connections system that allows users to create directional relationships between canvas items.
Connection points are available on:
- Groups: Container items that organize other canvas elements
- Sticky Notes: Text-based note items
- Text Blocks: Formatted text blocks
- Tables: Collaborative data tables
- Connection Point Discovery: Connection points appear on item edges when cursor is nearby (proximity-based with smooth opacity transitions)
- Creating Connections: Drag from a connection point on one item to a connection point on another item
- Directional Flow: Connections are directional (from source → to target)
- Visual Feedback: Active connection points highlight during drag operations
- A* Algorithm: Uses A* pathfinding to calculate optimal routes between connected items
- Obstacle Avoidance: Connection lines intelligently route around other canvas items
- Dynamic Updates: Paths automatically recalculate when items move or resize
- Waypoint Generation: Creates smooth paths with perpendicular exits/entries when possible
- Side Selection: Automatically chooses optimal connection sides based on item positions
- Connection Data: Stored persistently in SharedTree as arrays of connection IDs on each
Item - Visual Rendering:
ConnectionOverlay.tsxhandles all connection visualization - Geometry Utilities:
connections.tsprovides point calculations and side detection - Pathfinding Engine:
pathfinding.tsimplements A* algorithm with obstacle detection - Zoom Independence: Connection logic works in logical coordinates for consistent behavior at all zoom levels
These items are candidates for iterative improvement:
- Partial stroke erasing with segment splitting & gap smoothing
- Adjustable eraser size + visual radius slider
- Stroke simplification (Douglas–Peucker) for large point sets (store original + simplified)
- Pressure / velocity-based dynamic width (pen devices)
- Local persistence of preferred ink color/width (e.g.
localStorage) - Undo batching for multi-segment erasures or long strokes
| File | Purpose |
|---|---|
src/react/components/canvas/Canvas.tsx |
Main collaborative canvas (SVG + HTML layering, ink + overlays) |
src/react/overlays/ConnectionOverlay.tsx |
Visual connections with pathfinding and proximity-based UI |
src/utils/connections.ts |
Connection point calculations and side detection utilities |
src/utils/pathfinding.ts |
A* pathfinding algorithm for routing connections around obstacles |
src/react/hooks/useCanvasNavigation.ts |
Pan/zoom logic with discrete zoom steps & cursor anchoring |
src/presence/Interfaces/InkManager.ts |
Ephemeral ink presence contract |
src/presence/ink.ts |
Implementation utilities for broadcasting ink (if present) |
src/schema/app_schema.ts |
Persistent ink schema (InkStroke, InkPoint, InkStyle, InkBBox) |
The application is built with modern React and features a rich, interactive UI:
- React 18: Modern React with hooks and contexts
- Fluent UI: Microsoft's design system for consistent UX
- Tailwind CSS: Utility-first CSS framework for styling
- Vite: Fast build tool and development server
- TanStack Table: Advanced table functionality with virtual scrolling
- TanStack Virtual: Efficient virtualization for large datasets
- TypeScript: Type-safe development
- Canvas: Interactive workspace for shapes, notes, and tables
- Toolbar: Context-sensitive tools and actions
- Property Panels: Edit item properties and configurations
- Comments Pane: Threaded comments with voting
- Table Views: Rich data table interfaces with sorting and filtering
- Selection Indicators: Multi-user selection visualization
- Adaptive Layout: Works on desktop and tablet devices
- Zoom Support: Canvas zoom and pan functionality
- Touch Support: Touch-friendly interactions
- Keyboard Shortcuts: Efficient keyboard navigation
- Screen Reader Support: ARIA labels and semantic markup
- Keyboard Navigation: Full keyboard accessibility
- High Contrast: Support for high contrast themes
- Focus Management: Clear focus indicators
The application uses Tailwind CSS for styling alongside Fluent UI components:
- Utility-first: Use Tailwind utility classes for layout and styling
- Custom CSS: Additional CSS in
src/styles/for specialized needs - iOS Support: Special CSS fixes in
src/styles/ios-minimal.cssfor iOS Safari - Build Integration: Tailwind is integrated with Vite build process
To update styles during development, Tailwind CSS is automatically compiled by Vite. For manual compilation:
npx tailwindcss -i ./src/index.css -o ./src/output.css --watchThis sample application is configured to leverage the Fluid Framework's Developer Tooling.
Refer to the above article for examples and usage instructions.
You can use the following npm scripts (npm run SCRIPT-NAME) to build and run the app.
| Script | Description |
|---|---|
start |
Starts the development server (same as dev) |
dev |
Runs the app in development mode with Vite dev server |
dev:local |
Runs the app using local Fluid relay service |
dev:azure |
Runs the app using Azure Fluid Relay service |
dev:network |
Runs the app accessible from network devices |
dev:ios |
Runs the app with network access and shows IP |
build |
Builds the app for production (compile + webpack) |
compile |
Compiles TypeScript source code to JavaScript |
webpack |
Builds the app using Vite |
start:server |
Starts the local Azure Fluid Relay service |
format |
Formats source code using Prettier |
lint |
Lints source code using ESLint |
test |
Runs end-to-end tests with Playwright |
pretest |
Installs Playwright dependencies |
network-ip |
Shows network IP address for mobile testing |
-
Start Local Service (for local development):
npm run start:server
-
Start Development Server:
npm start # Uses default configuration npm run dev:local # Uses local Fluid service npm run dev:azure # Uses Azure Fluid Relay
-
Access the Application:
Open http://localhost:8080/ in multiple browser tabs to test collaboration features.
The application supports multiple authentication and hosting scenarios:
- Uses local Fluid relay service for development
- No authentication required
- Automatic user generation for testing
- Integrates with Azure Fluid Relay service
- Supports Microsoft Graph authentication
- Real user profiles and avatars
- Enterprise-grade collaboration features
Authentication and service configuration is handled in the src/infra/ folder:
azure/: Azure-specific client and token providersauth.ts: Authentication managementfluid.ts: Fluid Framework setuptokenProvider.ts: Token provider interfaces
src/
├── infra/ # Fluid Framework and authentication setup
├── react/ # React components and UI
│ ├── components/ # React components organized by feature
│ │ ├── app/ # Main App component
│ │ ├── canvas/ # Canvas and rendering components
│ │ ├── items/ # Item components (shapes, notes, tables)
│ │ ├── toolbar/ # Toolbar and button components
│ │ └── panels/ # Side panels and overlays
│ ├── contexts/ # React contexts for data management
│ ├── hooks/ # Custom React hooks
│ └── overlays/ # Presence, selection, and connection overlays
│ ├── ConnectionOverlay.tsx # Visual connections with pathfinding
│ └── ... # Other overlays
├── schema/ # SharedTree schema definitions
├── start/ # Application initialization
├── presence/ # Presence API implementations (signal-based)
│ ├── drag.ts # Real-time drag operations with signal optimization
│ ├── resize.ts # Shape resize collaboration with batched updates
│ ├── selection.ts # Multi-user selection tracking with state management
│ ├── users.ts # User management and profiles with auto-cleanup
│ ├── ink.ts # Ephemeral ink stroke management
│ └── Interfaces/ # TypeScript interfaces for presence and signals
├── undo/ # Undo/redo system
├── utils/ # Utility functions and managers
│ ├── connections.ts # Connection point calculations and geometry
│ ├── pathfinding.ts # A* pathfinding for connection routing
│ └── ... # Other utilities
├── styles/ # CSS files including Tailwind and iOS fixes
└── constants/ # Application constants and configuration
index.tsx: Application entry pointAppLoad.tsx: Application loader and initializationApp.tsx: Main UI component and application shellCanvas.tsx: Canvas component with SVG rendering and item interactionsConnectionOverlay.tsx: Visual connections system with pathfinding and proximity-based UIItemView.tsx: Canvas item rendering and interactions (includes presence indicators)TableView.tsx: Table component with virtual scrolling and presencecontexts/PresenceContext.tsx: React context for presence data with signal managementhooks/usePresenceManager.tsx: React hook for presence subscriptions with automatic optimization
This structure promotes modularity, type safety, and maintainable code organization for collaborative applications. The signal-based presence architecture ensures optimal performance while providing simple, developer-friendly APIs.