Are you happy with your logging solution? Would you help us out by taking a 30-second survey? Click here


A time traveling debugger for Cycle.js

Subscribe to updates I use cycle-time-travel

Statistics on cycle-time-travel

Number of watchers on Github 210
Number of open issues 17
Average time to close an issue 4 days
Main language JavaScript
Average time to merge a PR 2 days
Open pull requests 1+
Closed pull requests 0+
Last commit about 4 years ago
Repo Created about 4 years ago
Repo Last Updated over 1 year ago
Size 415 KB
Organization / Authorcyclejs
Page Updated
Do you use cycle-time-travel? Leave a review!
View open issues (17)
View cycle-time-travel activity
View on github
Fresh, new opensource launches 🚀🚀🚀
Trendy new open source projects in your inbox! View examples

Subscribe to our mailing list

Evaluating cycle-time-travel for your project? Score Explanation
Commits Score (?)
Issues & PR Score (?)

npm version Build Status Code Climate


cycle-time-travel is a time travelling stream viewer for Cycle.js apps.

Try the online example!

Why you should be excited:

  • It makes it easy to see data flowing through your
  • You can pause, and even rewind time by dragging on the stream bar
  • With hot module reloading, you could even fix your mistakes from the past!

A video is worth a thousand bullet points:

Okay, I'm in!

Great. Now just npm install cycle-time-travel and you can begin your mastery over time itself!

How do I use it?

The API is simple, and does two things. Displaying streams, and controlling time.

import makeTimeTravel from 'cycle-time-travel'

makeTimeTravel takes a DOM observable, and an array of streams to be displayed/controlled, in the form of {stream: stream$, label: 'stream$'}.

makeTimeTravel returns a DOM observable, and an object called timeTravel, with each of the streams you provided as an argument to makeTimeTravel available, keyed under the label.

Huh? Show me an example

Here is the counter example from the Cycle.js docs.

import Cycle from '@cycle/core';
import {h, makeDOMDriver} from '@cycle/dom';

function main({DOM}) {
  let action$ = Cycle.Rx.Observable.merge('.decrement').events('click').map(ev => -1),'.increment').events('click').map(ev => +1)

  let count$ = action$.startWith(0).scan((x,y) => x+y);

  return {
    DOM: count$.map(count =>
        h('div', [
          h('button.decrement', 'Decrement'),
          h('button.increment', 'Increment'),
          h('p', 'Counter: ' + count)
}, {
  DOM: makeDOMDriver('#app')

And here it is with time travelling:

import Cycle from '@cycle/core';
import {h, makeDOMDriver} from '@cycle/dom';
import makeTimeTravel from 'cycle-time-travel';                 // NEW

function main({DOM}) {
  let action$ = Cycle.Rx.Observable.merge('.decrement').events('click').map(ev => -1),'.increment').events('click').map(ev => +1)

  let count$ = action$.startWith(0).scan((x,y) => x+y);

  let {DOM: timeTravelBar$, timeTravel} = makeTimeTravel(DOM, [ // NEW
    {stream: count$, label: 'count$'},                          // NEW
    {stream: action$, label: 'action$'}                         // NEW
  ]);                                                           // NEW

  return {
    DOM: Cycle.Rx.Observable.combineLatest(                     // NEW
      timeTravel.count$,                                        // NEW
      timeTravelBar$,                                           // NEW
      (count, timeTravelBar) =>                                 // NEW
        h('.app', [                                             
          h('div', [                                            
            h('button.decrement', 'Decrement'),
            h('button.increment', 'Increment'),
            h('p', 'Counter: ' + count)

          timeTravelBar                                         // NEW
}, {
  DOM: makeDOMDriver('#app')

There are a few things going on above:

  • We call makeTimeTravel, passing in DOM, count$ and action$.
  • We get back a timeTravelBar DOM observable, and a timeTravel object with count$ and action$.
  • We then swap out the count$ that was being used to power the view with timeTravel.count$
  • We also have to combineLatest and add the timeTravelBar to the DOM that we return

That seems like a lot of work...

It might, but try it anyway! cycle-time-travel is in alpha so the API is still under development. If you have any feedback on how it could be easier to use, I'd love to hear it.

For more examples, see the /examples folder.


cycle-time-travel is available under the MIT license. See the LICENSE file for full text.


I encourage and welcome contributions, be it pull requests, issues or just feedback. If in doubt, get in touch with me at

cycle-time-travel open issues Ask a question     (View All Issues)
  • over 3 years Support Cycle.js Diversity
  • over 3 years Consider a browser extension instead of UI timeTravelBar$
  • almost 4 years Update cycle dependencies
  • about 4 years Add a show/hide toggle
  • about 4 years Add tests using marble pattern
  • about 4 years Investigate visualizing observable state tree
  • about 4 years Major breaking change suggestion
  • about 4 years Fix streams only showing up after first event (should be visible from start)
  • about 4 years Fix inserting events into the past
  • about 4 years Add a sidebar to filter displayed streams
  • about 4 years Suggestion to make API simpler
  • about 4 years Add export to unit test
  • about 4 years Add dragging momentum
  • about 4 years Proper documentation
  • about 4 years Investigate hot module loading
  • about 4 years Add an integration test
cycle-time-travel open pull requests (View All Pulls)
  • [WIP] New interface
cycle-time-travel list of languages used
Other projects in JavaScript