Case study 02 · Contract · ran concurrently with Cura Healthcare
DataPure AI
Built the client-side annotation application from the ground up with React, the Canvas API, and Konva.js, handling 9,000+ shapes within a single workspace.
- Role
- Front-end Developer (Contract)
- Period
- Aug 2022 – Nov 2022
- Context
- Contract · ran concurrently with Cura Healthcare
- React
- Next.js
- TypeScript
- Konva.js
- Canvas API
- Redux
- Node.js
- MongoDB
Context
- A single annotation workspace renders and manipulates 9,000+ shapes at once.
- Annotation workflows involved simultaneous operations with consistent UI state across them.
What I built
- Built the annotation workspace
- Built the client-side application from the ground up with React, the Canvas API, and Konva.js.
- Designed the Redux state layer
- Designed Redux state management for complex annotation workflows, keeping UI state consistent across simultaneous operations.
- Redesigned the drawing workflow
- Reworked the drawing flow and added keyboard-shortcut support in the React interface.
- Added persistence across the stack
- Contributed full-stack features for shape persistence and retrieval using Next.js API routes, Node.js, and MongoDB.
Outcome
- 9,000+shapes in a single workspace
- 80%improvement in annotator productivity
Illustrative technique
A generic React and Konva example, built for this portfolio. It renders thousands of synthetic shapes on one canvas so you can see how density and interaction are handled at scale. It is not a reproduction of any client product, and it uses no client assets or data. Selecting a shape on the canvas is optional; the controls below do the same job by keyboard.
900 synthetic shapes rendered. Use the Previous and Next shape buttons, or select a shape on the canvas, to inspect one.
Techniques on show: a single Konva layer, hit-testing disabled on the backdrop, and per-shape shadow and perfect-draw passes turned off. Slider changes are committed with the Render button, so the canvas does not regenerate on every input event. The view updates only in response to your input, so nothing animates on its own and reduced-motion preferences are respected by default.