{"id":51714365,"url":"https://github.com/tailflare/tempa","last_synced_at":"2026-07-17T01:05:28.890Z","repository":{"id":368975340,"uuid":"1286928770","full_name":"tailflare/tempa","owner":"tailflare","description":"A Rust library for working with time in animation and real-time systems.","archived":false,"fork":false,"pushed_at":"2026-07-10T21:02:05.000Z","size":51,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-07-10T23:04:47.557Z","etag":null,"topics":["animation","gamedev","math","no-std","rust","rust-lang","rust-library"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tailflare.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-02T08:15:50.000Z","updated_at":"2026-07-10T21:02:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tailflare/tempa","commit_stats":null,"previous_names":["tailflare/tempa"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/tailflare/tempa","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tailflare%2Ftempa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tailflare%2Ftempa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tailflare%2Ftempa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tailflare%2Ftempa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tailflare","download_url":"https://codeload.github.com/tailflare/tempa/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tailflare%2Ftempa/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35563099,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-16T02:00:06.687Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animation","gamedev","math","no-std","rust","rust-lang","rust-library"],"created_at":"2026-07-17T01:05:28.281Z","updated_at":"2026-07-17T01:05:28.881Z","avatar_url":"https://github.com/tailflare.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tempa\n\n[![Tag](https://img.shields.io/github/v/tag/tailflare/tempa)](https://github.com/tailflare/tempa/tags)\n[![Crates.io Version](https://img.shields.io/crates/v/tempa)](https://crates.io/crates/tempa)\n[![docs.rs](https://img.shields.io/docsrs/tempa)](https://docs.rs/tempa)\n[![Main CI Build Status](https://img.shields.io/github/actions/workflow/status/tailflare/tempa/ci.yml?label=main%20build)](https://github.com/tailflare/tempa/actions/workflows/ci.yml)\n[![License](https://img.shields.io/crates/l/tempa)](#license)\n\n\nA no_std library for working with time in animation and real-time systems.\n\nIt provides a small set of temporal primitives: time, duration, time ranges, frame rates, and frame-based stepping. Designed around deterministic conversion between continuous time and discrete frame indices.\n\nThis makes it suitable for animation systems, simulations, and engine tooling where frame-accurate behavior matters.\n\n## Status\n\n⚠️ Experimental / Work in Progress\n\nThe API is still evolving and may change significantly.\n\n## Features\n\n- no_std compatible\n- Time and Duration types\n- Frame rate representation\n- Time ranges and range operations\n- Deterministic frame \u003c-\u003e time conversion\n- Frame-based stepping utilities\n\n## Usage\n### std\n```toml\ntempa = \"0.1.9\"\n```\n\n### no_std\n```toml\ntempa = { version = \"0.1.9\", default-features = false }\n```\n\n### Example\n```rust\n// Create a frame stepper running at 60 FPS and a simple 10-second clip.\nlet rate = FrameRate::from_fps(60.0_f64);\nlet mut stepper = FrameStepper::\u003cu32, f64\u003e::new(rate);\nlet clip = TimeRange::new(Time::from_seconds(0.0_f64), Time::from_seconds(10.0_f64));\n\n// Advance the playhead by 120 frames (2 seconds at 60 FPS).\nstepper.step(120);\nassert_eq!(stepper.current(), FrameIndex::new(120_u32));\nassert_approx_eq_abs_tol!(stepper.current_time().seconds(), 2.0_f64, 1e-12_f64);\n\n// Convert explicitly between discrete frames and continuous time.\nlet frame: FrameIndex\u003cu32\u003e = Time::from_seconds(1.25_f64).to_frame(rate);\nassert_eq!(frame, FrameIndex::new(75_u32));\n\nlet time = Time::from_frame(frame, rate);\nassert_approx_eq_abs_tol!(time.seconds(), 1.25_f64, 1e-12_f64);\n\n// Seek to an arbitrary time. Times between frame boundaries are rounded\n// down to the frame currently containing that instant.\nstepper.seek_time(Time::from_seconds(2.75_f64));\nassert_eq!(stepper.current(), FrameIndex::new(165_u32));\nassert_approx_eq_abs_tol!(stepper.current_time().seconds(), 2.75_f64, 1e-12_f64);\n\n// Normalize the playhead position within the clip.\nlet u = clip.normalize_time(stepper.current_time());\nassert_approx_eq_abs_tol!(u, 0.275_f64, 1e-12_f64);\n\n// Remap the same normalized position into another time range.\nlet outro = TimeRange::new(Time::from_seconds(8.0_f64), Time::from_seconds(10.0_f64));\nlet outro_time = clip.remap_time(stepper.current_time(), \u0026outro);\nassert_approx_eq_abs_tol!(outro_time.seconds(), 8.55_f64, 1e-12_f64);\n\n// Construct ranges directly from scalar seconds.\nlet window: TimeRange\u003cf64\u003e = (1.0..3.5).into();\nassert_approx_eq_abs_tol!(window.duration().seconds(), 2.5_f64, 1e-12_f64);\n\n// Merge overlapping ranges, for example when combining timeline edits.\nlet a = TimeRange::new(Time::from_seconds(1.0_f64), Time::from_seconds(4.0_f64));\nlet b = TimeRange::new(Time::from_seconds(3.5_f64), Time::from_seconds(6.0_f64));\nlet merged = a.union(\u0026b);\nassert_eq!(merged, TimeRange::new(Time::from_seconds(1.0_f64), Time::from_seconds(6.0_f64)));\n\n// Stepping past the beginning of the timeline saturates at frame zero.\nstepper.step(-10_000);\nassert_eq!(stepper.current(), FrameIndex::ZERO);\nassert_approx_eq_abs_tol!(stepper.current_time().seconds(), 0.0_f64, 1e-12_f64);\n```\n\n## License\n\nThe Tempa project is licensed under either the Apache License, Version 2.0 or the MIT license, at your option.\n\nSee [LICENSE-APACHE](./LICENSE-APACHE) and [LICENSE-MIT](./LICENSE-MIT) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftailflare%2Ftempa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftailflare%2Ftempa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftailflare%2Ftempa/lists"}