TimePad is a basic recordable and replayable textarea (More of an algorithm for a recordable and replayable textarea), that can be used for all sorts of stuff such as a recordable code lecture paired with Audio guides (Used in Hobnob), coding interviews or basically wherever your creativity can find a use for it.
A basic example of the implementation of Timepad is provided at this place, one could use the following algorithm to replicate TimePad in any environment and any language that can use something like this.
I noticed since I last published a doc about this algorithm that there were things I could improve on for efficiency and speed.
Once you're done reading the algorithm, head on over to the Improvements section.
Timepad can be replicated in the environment of your choice with the following algorithms:
-
Setup a
textareaor any input equivalent that can accept text and has event handlers forchanges,selectionsandcursor movementsdepending on your needs. -
Set
currentTimeto0in the beginning.
window.currentTime = 0;- Similarly, create an array, preferrable named
recordedChangesthat is an empty array.
window.recordedChanges = [];-
Add buttons or handlers for play, pause and terminate recording. You can even conditionally render them based on the state (For example, one might want to hide the terminate button while the textarea is recording).
-
On starting of recording, toggle a function that increases the value of
currentTimeroughly 20-30 times a second, by a margin of1000ms/(Increments per second)for smooth recording (20fps or 30fps or even more, but anything over 30fps is basically unnecessary).
function incrementTime(fps) {
if (fps && parseInt(fps)) window.currentTime += 1000 / parseInt(fps);
}
// Attaching the above function to an interval.
window.fps = 30;
window.timer = window.setInterval(function () {
incrementTime(window.fps);
}, window.fps);
// Use the window.timer variable to clearInterval on pause and terminate.- On cursor movement (Which includes selection of text) or change in the text of the textarea, have a trigger function record the following things: Cursor Position, Selection Range and Text Content of the textarea, bundle them in an object and push them to
recordedChangesarray along with the time of change.
function addTowindow.recordedChanges(text, cursor, selection){
return window.recordedChanges.push({
time: currentTime, // the time that was being incremented.
text,
cursor,
selection
});
};(See js/helpers.js and js/scripts.js for an example of these functions.)
-
On stop or pause, pause the incrementer function as well so the times when recording was paused are not considered.
-
Once done, use the pause/stop button to stop recording. Log the
recordedChangesarray to get the changes with time. You can store it on a server for replaying later on or export it to simply replay it later on a separate textarea using the algo in the upcoming section.
Optional step:
-
In the question of scale, it's already a very efficient solution since it is much smaller in comparison to video recording a textarea in the first place. However, if you are still worried. Here are some solutions:
- Have the recording streamed instead of being thrown to the client or server all at once. Especially in case of long recordings that can have a lot of text in them.
- Use
nullvalues in case there isn't a selection, instead of storing the entire selection objects. - Reduce the length of the keys used to store the data in the objects, for example:
{ ti: currentTime, t: text, c: cursor, s: selection }As a result you would be storing a string of less length in case you decide to follow the following step.
- You could also choose to store a Stringified version of
recordedChangesin order to reduce the memory when stored on the server and parse it as objects once received by the frontend.
// While storing in the backend let dataToSend = JSON.stringify(window.recordedChanges); // While using the same recording received from the backend. let dataToReplay = JSON.parse(receivedStringArray);
Once the recordedChanges array is received from the recorded textarea, you can replay it by following the enlisted steps.
-
Just like recording, set the
currentTimeto 0 in the beginning and addrecordedChangesas a global variable for the timebeing. -
Setup a textarea or another input equivalent that you can control the flow and content of using functions.
-
Add appropriate Start, Reset and Pause buttons. The start button fires the interval function that increments the
currentTimeby a certain margin and at a certain time like in step 5 of Recording. -
Setup a binary search function for quick finding of the time related object.
function searchForTime(timeToSearch = 0) {
// Function that uses binary search to search for occurances of the time in the array.
if (
window.recordedChanges[window.recordedChanges.length - 1].time <
timeToSearch
)
return -2; // The recording has ended.
let min = 0,
max = window.recordedChanges.length - 1;
while (min <= max) {
mid = parseInt((min + max) / 2);
if (window.recordedChanges[mid].time === timeToSearch)
return window.recordedChanges[mid];
else if (window.recordedChanges[mid].time > timeToSearch) max = mid - 1;
else min = min + 1;
}
return -1;
}-
Setup a function to apply the changes of the found object by time to the textarea. Look at an example of it in
js/scripts.js(applyChanges). -
In the function you setup as an interval function in step 3, add a code snippet that searches for the instance of
currentTimein therecordedChangesarray, and applies the changes if it finds one. Similarly, stop the replay if you get a-2return from the binary search function above. -
On pause, clear the interval to be setup again when the user clicks play. On reset, reset the
currentTimeto 0 and clear the interval. -
There you go, you now have a recordable and replayable textarea.
The code that is used for the implementation isn't exactly something you would put into a testing environment right on the go, it just serves as an example and can break. I would be pretty happy if you break it and let me know in the issues section of the repository.
I am pretty sure this is not the only algo like this out there, for sure. However, if you choose to use this in any of your services, projects or projects. Do let me know at my email. I would be more than pleased to know.
-
Unlike the initial version (below), we will not be storing the entire text from a textarea at a point in time, and instead store diffs between them at any given changepoint.
-
We can remove our reliance on
setIntervalat recording time and instead useeventsto track changes, selections and cursor movements and add it to a change stack. Round off the time to the closest
For any issues you may face or have suggestions that might improve the algo and TimePad, just open an issue or make the changes you may want to the algo and open a pull request for the Repo, improvements deemed worthy will be merged or considered for any future changes.