A Client-Side Browser-Based Video Editor for Free.
A 100% client‑side video micro editor that runs entirely in your browser.
You can:
- Trim a time range
- rectangle crop
- Export the clip
No server is required. Everything is done with HTML5 Video + Canvas + MediaRecorder.
- Choose a video file.
- Set the crop:
- Move the cursor over the video preview.
- Click & drag to draw a rectangle (crop area).
- Double‑click inside the preview to reset the crop.
- Adjust the trim range:
- Move Start/End on the slider, or
- Play the video and click:
- Use current time as Start
- Use current time as End
- Click Export.
- Wait until export finishes:
- No file is ever uploaded to a server.
- All processing is done locally in your browser tab.
- Safe for private recordings, screen captures, etc.
# Build the image
docker compose build
# Run the container
docker compose up
docker compose \
-f docker-compose.test.yml up \
--build --exit-code-from \
frontend_test- Frontend: React + TypeScript + Vite
- Styling: Plain CSS (
src/style.css) - Tests: Jest + ts‑jest
- Container: Docker / Docker Compose for reproducible dev & tests
-
File input
- User selects/drops a file -> stored as
Filein React state.
- User selects/drops a file -> stored as
-
Metadata & dimensions
onLoadedMetadatareads:video.durationvideo.videoWidth,video.videoHeight
- These are used for:
- Trim slider range (0 → duration)
- Converting crop coordinates from CSS space → video pixel space.
-
Trim range
- Two numeric states:
trimStart: numbertrimEnd: number
- Slider & numeric inputs are kept in sync.
- Constraints:
0 ≤ trimStart ≤ trimEnd ≤ duration
- Two numeric states:
-
Crop rectangle
- Pointer events on
<video>:pointerdown: begin croppointermove: update crop during dragpointerup/pointerleave: finalize crop
- Coordinates are normalized by the video’s bounding box and scaled to the native resolution:
- Pointer events on
-
Export
canvas.width / height= crop width/height (or full video size).canvas.captureStream(fps)obtains aMediaStream.- Frames are drawn with
requestAnimationFrameuntil:now >= endTimeorvideo.currentTime >= trimEnd
- When finished:
MediaRecorder.stop()- Chunks are combined into a
Bloband downloaded.
- Apache License 2.0
