Frames to timecode
At 30 frames per second, frame 1,800 is exactly one minute, written 00:01:00:00, and frame zero is always 00:00:00:00. Paste the label straight onto a cue sheet or a caption review. Free in your browser.
- Free
- Runs in your browser
- No sign-up
- Your data stays on your device
Run — free
Worked example
- Frames
- 1,800
- Fps
- 30
- Hours
- 0
- Minutes
- 1
- Seconds
- 0
- Frames
- 0
A frame index is the native clock of every nonlinear editor, render farm, and caption writer, but humans read hours, minutes, seconds, and leftover frames. This frames-to-timecode converter accepts a non-negative integer frame count and a positive frame rate in frames per second and returns those four components plus a zero-padded HH:MM:SS:FF label. Counting is zero-based: frame zero is 00:00:00:00, and at thirty frames per second one thousand eight hundred frames is exactly one minute.
How a frame count becomes HH:MM:SS:FF
Non-drop timecode labels each picture as hours, minutes, seconds, and a frame digit inside the current second. At a constant positive frame rate fps, every complete second holds exactly that many frames, every minute holds sixty seconds, and every hour holds three thousand six hundred seconds. Starting from a total frame count N, the leftover frame digit is the remainder of N divided by fps, and the whole-second clock is the integer quotient floor of N divided by fps.
Those whole seconds then split with ordinary clock arithmetic: seconds are the quotient modulo sixty, minutes are the next quotient modulo sixty, and hours are whatever remains. Frame zero is always 00:00:00:00. At twenty-four frames per second, frame twenty-four is 00:00:01:00, and frame one thousand eight hundred at thirty frames per second is 00:01:00:00.
Where a frames-to-timecode calculator earns its keep
Assistant editors often receive a render range or a marker list as raw frame numbers from a farm, a conform, or a statistics dump, and they need a human label they can read on a cue sheet or paste into a comment. Caption and subtitle pipelines convert integer in and out points into HH:MM:SS:FF so a review can talk in the same clock the picture department uses.
QC scripts can assert that a computed label matches the project fps before a package leaves the facility, catching a wrong-rate assumption that would otherwise shift every cue by a fraction of a second. Film-school labs and LMS plugins use the free browser path so students practice zero-based remainder arithmetic without installing desktop calculators that disagree on whether frame zero is the first picture.
Keep fps explicit, treat frame zero as 00:00:00:00, and you get a reproducible label for notes, DAILIES, and edge workers without standing up a full NLE just to divide a count by a rate.
What you can do with it
Farm ranges to readable labels
Turn integer render in/out points from a farm or conform into zero-padded HH:MM:SS:FF that assistants can paste onto a cue sheet.
Caption in/out in picture time
Convert subtitle frame indices into hours, minutes, seconds, and leftover frames at the deliverable fps so review talks in the picture clock.
Classroom remainder drills and CI pins
Give students and test suites a deterministic non-drop oracle that maps frame zero to 00:00:00:00 and rejects a non-positive rate.
FAQ
How do I convert a frame count to timecode?
Send a non-negative integer frames and a positive fps.
What happens if the frame rate is not positive?
Zero and negative rates are rejected and are not billed.
Is frame zero equal to 00:00:00:00?
Yes. Counting is zero-based. Frame zero at any positive fps is 00:00:00:00; the leftover frame digit runs from 0 through fps minus one on integer rates.
Does this converter use drop-frame timecode?
No. Arithmetic is non-drop only. Fractional rates are converted through total seconds, but NTSC drop-frame skip tables are not applied.
What timecode is frame 24 at 24 fps?
It is 00:00:01:00. Counting starts at zero, so the first 24 frames, numbered from zero, fill the first second, and frame 24 opens the next one.
Related tools
For developers — API access
Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.
API endpoint
Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.
Call it from your stack
curl -X POST https://api.kit.forhosting.com/video/frames-to-timecode \
-H "Authorization: Bearer $KIT_KEY" \
-H "Content-Type: application/json" \
-d '{"frames":1800,"fps":30}'const res = await fetch("https://api.kit.forhosting.com/video/frames-to-timecode", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.KIT_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"frames": 1800,
"fps": 30
})
});
const { task_id } = await res.json();import os, requests
res = requests.post(
"https://api.kit.forhosting.com/video/frames-to-timecode",
headers={"Authorization": f"Bearer {os.environ['KIT_KEY']}"},
json={
"frames": 1800,
"fps": 30
},
)
task_id = res.json()["task_id"]<?php
$res = file_get_contents("https://api.kit.forhosting.com/video/frames-to-timecode", false, stream_context_create([
"http" => [
"method" => "POST",
"header" => "Authorization: Bearer " . getenv("KIT_KEY") . "\r\nContent-Type: application/json",
"content" => '{"frames":1800,"fps":30}',
],
]));
$task = json_decode($res, true);body := bytes.NewBufferString(`{"frames":1800,"fps":30}`)
req, _ := http.NewRequest("POST", "https://api.kit.forhosting.com/video/frames-to-timecode", body)
req.Header.Set("Authorization", "Bearer "+os.Getenv("KIT_KEY"))
req.Header.Set("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)Example request
{
"frames": 1800,
"fps": 30
}Example response
{
"task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
"type": "video.frames_to_timecode",
"status": "queued",
"_links": {
"result": "/tasks/tsk_…/result"
}
}The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.
Pricing
Published price — no tokens, no invented credits. A failed task is never charged.
Limits
max_mb | 500 |
max_minutes | 60 |
max_megapixels | 3.9 |
Errors
| HTTP | Code | Meaning |
|---|---|---|
401 | unauthorized | Missing or invalid API key. |
402 | insufficient_balance | Your balance doesn't cover the task price. |
404 | unknown_type | That task type doesn't exist. |
429 | rate_limited | Too many requests. Use the webhook instead of polling. |
Technical notes
If the frame rate is zero, negative, or otherwise not positive, the task fails with invalid_input instead of dividing by zero or inventing a clock.
How a frame count becomes HH:MM:SS:FF
This endpoint implements that non-drop identity only. It does not apply NTSC drop-frame skip tables for twenty-nine point nine seven, because those rules change the written label without using the same remainder formula. Choose the positive fps that matches the project (twenty-four, twenty-five, thirty, forty-eight, fifty, sixty, and similar integer rates, or a fractional rate when you truly need one) and keep that rate explicit on every request so two tools never assume a default behind your back.
Input fields, validation, and the JSON you get back
Numeric strings are accepted for both. Zero, negative numbers, and non-finite values are rejected with a message that the frame rate must be positive, and those failures are not billed. Example: frames one thousand eight hundred and fps thirty yields hours zero, minutes one, seconds zero, frames zero, and timecode 00:01:00:00. A second example, frames one thousand one at twenty-four fps, yields 00:00:41:17 because one thousand one divided by twenty-four is forty-one seconds with remainder seventeen. Hours pad to at least two digits and may grow beyond ninety-nine; minutes, seconds, and the frame digit pad to two. POST a JSON body with two required fields: frames, a non-negative integer total frame count, and fps, a positive finite frame rate in frames per second. Aliases frames, frame_count, total_frames, and n all feed the count; aliases fps, frame_rate, framerate, and rate all feed the rate. Missing fields, empty strings, non-numeric values, and a negative frame count fail with invalid_input and name the field. The hard product rule is the rate: fps must be strictly greater than zero. On success the body returns hours, minutes, seconds, and frames (the leftover digit FF), a normalized zero-padded timecode string, the echoed fps, the original total_frames, and a short formula string restating the non-drop remainder identity. Optional aliases let scripts keep familiar names without a mapping layer.
Technical questions
What does the frames-to-timecode converter cost?
The browser tool is free. API requests use prepaid KIT balance at $0.002 per successful call; invalid input is not billed.