{"id":27414476,"url":"https://github.com/willkessler/jupytergraffiti","last_synced_at":"2025-04-14T08:25:09.193Z","repository":{"id":34147798,"uuid":"143356605","full_name":"willkessler/jupytergraffiti","owner":"willkessler","description":"Create interactive screencasts inside Jupyter Notebook that anybody can play back","archived":false,"fork":false,"pushed_at":"2024-06-11T09:37:52.000Z","size":224494,"stargazers_count":144,"open_issues_count":8,"forks_count":18,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-21T10:19:50.774Z","etag":null,"topics":["annotations","draw-lines","graffiti-extension","highlight","inline","jupyter-graffiti","jupyter-notebook","movie","notes","screencasts","shells","terminals"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/willkessler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"COPYING.md","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}},"created_at":"2018-08-03T00:07:35.000Z","updated_at":"2024-12-30T22:23:43.000Z","dependencies_parsed_at":"2024-02-17T02:38:49.240Z","dependency_job_id":null,"html_url":"https://github.com/willkessler/jupytergraffiti","commit_stats":{"total_commits":1074,"total_committers":4,"mean_commits":268.5,"dds":0.05027932960893855,"last_synced_commit":"0bbd87cefb0a12696fe27088aff435d8dcd81268"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willkessler%2Fjupytergraffiti","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willkessler%2Fjupytergraffiti/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willkessler%2Fjupytergraffiti/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/willkessler%2Fjupytergraffiti/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/willkessler","download_url":"https://codeload.github.com/willkessler/jupytergraffiti/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248844507,"owners_count":21170584,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["annotations","draw-lines","graffiti-extension","highlight","inline","jupyter-graffiti","jupyter-notebook","movie","notes","screencasts","shells","terminals"],"created_at":"2025-04-14T08:25:08.519Z","updated_at":"2025-04-14T08:25:09.169Z","avatar_url":"https://github.com/willkessler.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Jupyter Graffiti\n\nCreate interactive screencasts inside Jupyter Notebook that anybody can play back.\n\n![intro_movie](./images/tutorial/graffitiMoviePlay1.gif)\n\nEver wanted to offer someone a hands-on \"live\" demo in Jupyter\nNotebook? Using __Graffiti__ you can add floating tips along with\noptional recorded movies anywhere in your Notebooks. Demonstrate\nwhatever you want, highlight what's important-- all narrated by you!\nSince the movie plays back in and _on_ the Notebook, the viewer can\npause any time, dive into your code and try things out exactly as you\nhad them when you were recording, and then resume play.\n\n## Is this project up to date?\n\nYes, Graffiti is maintained and kept up to date by its author. Please\nfile issues as you see them or submit PRs. It's most recently been\nupdated for security and to eliminate lodash dependencies, on January 2, \n2023.\n\n** Update 1/11/2024: Graffiti has been upgraded to run in Jupyter\nNotebooks V7, but in \"classic\" mode. You will have to run your\nnotebooks in Classic mode to use Graffiti until it gets completely\nrewritten for JupyterLab. I am not intending to make that update\nunless I hear back from the community that it's desired. **\n\n\n## Try Graffiti Out! Try These _Live_ Demos:\n\n* [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/willkessler/jupytergraffiti/master?urlpath=/tree/samples%2Fgeneral%2FGraffiti%20Basic%20Demo.ipynb) \u003ca href=\"https://mybinder.org/v2/gh/willkessler/jupytergraffiti/master?urlpath=/tree/samples%2Fgeneral%2FGraffiti%20Basic%20Demo.ipynb\"\u003eGeneral Demo\u003c/a\u003e\n* [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/willkessler/jupytergraffiti/master?urlpath=/tree/samples%2Fudacity%2Fc%2B%2B_nanodegree%2FFor%20Loops.ipynb) \u003ca href=\"https://mybinder.org/v2/gh/willkessler/jupytergraffiti/master?urlpath=/tree/samples%2Fudacity%2Fc%2B%2B_nanodegree%2FFor%20Loops.ipynb\"\u003eUdacity C++ Nanodegree Example: \"For Loops\"\u003c/a\u003e\n* [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/willkessler/jupytergraffiti/master?urlpath=/tree/samples%2Fudacity%2Fc%2B%2B_nanodegree%2FPointers.ipynb) \u003ca href=\"https://mybinder.org/v2/gh/willkessler/jupytergraffiti/master?urlpath=/tree/samples%2Fudacity%2Fc%2B%2B_nanodegree%2FPointers.ipynb\"\u003eUdacity C++ Nanodegree Example: \"Pointers\"\u003c/a\u003e\n* [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/willkessler/jupytergraffiti/master?urlpath=/tree/samples%2Fudacity%2Fdata_structures_nanodegree%2FImplement%20a%20stack%20using%20an%20array.ipynb) \u003ca href=\"https://mybinder.org/v2/gh/willkessler/jupytergraffiti/master?urlpath=/tree/samples%2Fudacity%2Fdata_structures_nanodegree%2FImplement%20a%20stack%20using%20an%20array.ipynb\"\u003eUdacity Data Structures and Algorithms Nanodegree Example: \"Implementing a Stack using an Array\"\u003c/a\u003e\n\n_Please wait about 30 seconds for the demonstration to spin up at mybinder.org. (Thanks to the folks at MyBinder for this awesome service)._\n\n## Or, Watch Regular Recorded Video Demos:\n\n* \u003ca target=\"_blank\" href=\"https://youtu.be/3hGD0PiPuhI\"\u003eGentle Introduction To Graffiti\u003c/a\u003e (Start here)\n* \u003ca target=\"_blank\" href=\"https://www.youtube.com/watch?v=8gDmr5MiIVA\"\u003eHow to make Graffiti movies.\u003c/a\u003e\n* \u003ca target=\"_blank\" href=\"https://www.youtube.com/watch?v=Mwh5rCRXar4\"\u003eHow to make a Graffiti tooltip in code\u003c/a\u003e\n* \u003ca target=\"_blank\" href=\"https://www.youtube.com/watch?v=PUD-Juu0ZQE\u0026t=9s\"\u003eUsing Graffiti extras like in-line terminals and auto-saving code cells.\u003c/a\u003e\n* \u003ca target=\"_blank\" href=\"https://www.youtube.com/watch?v=hjU0gmis078\"\u003eAdding a recording to a Show/Hide solution button.\u003c/a\u003e\n* \u003ca target=\"_blank\" href=\"https://www.youtube.com/watch?v=3Zu2Wu44N_I\u0026t=1s\"\u003eHow Graffiti in-line shells are recorded into movies.\u003c/a\u003e\n* \u003ca target=\"_blank\" href=\"https://www.youtube.com/watch?v=vBNHcqJCW-Q\"\u003eAccessing and using the Graffiti API\u003c/a\u003e\n\n## What exactly can you record in the Graffiti?\n\n* Recorded audio (e.g. voice narration), captured with your laptop's microphone while making your recording\n* Mouse movement and scrolling in the Notebook\n* Selecting and editing inside code cells\n* The output of any code cell executions\n* Inlined terminals (shells) whose activities you can also record.\n* You can also draw and highlight over sections you think are important, or create handwritten notes.\n* Support for the \u003ca href=\"https://github.com/QuantStack/xeus-cling\"\u003eC++ kernel\u003c/a\u003e and the \u003ca href=\"https://github.com/IRkernel/IRkernel\"\u003eR kernel\u003c/a\u003e are included in addition to the Python kernel.\n\n## But, does this extension work in Jupyter Lab?\n\nNo, it only works in Jupyter Notebook classic. It would be quite\ncomplex to port to Lab as it has an entirely different internal API.\nHowever, if anybody in the community wants to see a port and\ncollaborate, please reach out.\n\n## Learn More On Using Graffiti:\n\n* \u003ca target=\"_blank\" href=\"https://drive.google.com/file/d/1eEga9NJ4ak8RZh28co4CZRck-4TrdjsE/view\"\u003eHow to make Graffiti movies\u003c/a\u003e\n* \u003ca target=\"_blank\" href=\"https://drive.google.com/file/d/1VP5U8xOYsS9rRM4-nc7IjKiLkubBu5Q3/view\"\u003eHow to make a Graffiti tooltip in code\u003c/a\u003e\n* \u003ca target=\"_blank\" href=\"https://drive.google.com/file/d/1y3bXMF5nvUYP9E67SATotRw8m_TRjg4O/view\"\u003eAccessing and using the Graffiti API\u003c/a\u003e\n* \u003ca target=\"_blank\" href=\"https://drive.google.com/file/d/1i6QuWlHfLNu62d0l1CIteiP-1yNqaHuY/view\"\u003eUsing Graffiti extras like in-line terminals and auto-saving code cells\u003c/a\u003e\n* \u003ca target=\"_blank\" href=\"https://drive.google.com/file/d/1N2gyzCN14b1vTwZBxNjsgNhZklku-qq4/view\"\u003eAdding a recording to a Show/Hide solution button\u003c/a\u003e\n* \u003ca target=\"_blank\" href=\"https://drive.google.com/file/d/1ROb1dCp-w4Js0P77-YcqRWHf0Y0hh0c8/view\"\u003eHow Graffiti in-line shells are recorded into movies\u003c/a\u003e\n\nYou can also visit the \u003ca href=\"user_manual/UserManual.ipynb\"\u003eUser Manual\u003ca\u003e for more detailed instructions on how to use Graffiti.\n\n## Advantages of Graffiti Over Traditional Screencasts\n\n* You can save any number of Graffitis in a Notebook.\n* You don't need any special software or hardware (other than this library and Chrome/Firefox) to create and save Graffitis.\n* Viewers can pause recorded playback any time, scrub forward and\nbackward, and interact with the Notebook during playback at any\npoint. No need to watch a whole recorded screencast first, and then\nswitch context to a Notebook; students can explore right along with\nyou in the same environment you recorded in. When you pause playback,\nyou're still in a live Notebook, so you can play around.\n* Jupyter Graffiti is easy to set up: either use the Python library\n  or build the Docker image with the included Jupyter extension.  (At\n  Udacity, Jupyter Notebook Workspaces use the extension. See below\n  how to install it). Or, you can skip installation entirely (see below).\n* All data, including audio, is stored in compressed plain text in a\n  directory separate from your Notebook files, for easy portability and\n  storage in any version control system e.g. git/github.\n* Unlike streamed video, you don't need a video server or hosted\n  YouTube videos, and you can watch the videos even without an\n  internet connection or over narrow bandwidth, because the files are\n  very small.\n\n## Using Graffiti with ZERO installation\n\nYou can skip installation entirely if you want, by using the Binder\ndemo link above. Just upload whatever Notebook you want to add\nGraffiti to the demo Jupyter Notebook server, activate Graffiti\n(see below), make some recordings, and then download the\n`jupytergraffiti_data` folder along with your Graffiti-ized Notebook\nfrom the binder Notebook server. To make this easier, we've installed\n`nbzip` into the Binder demo server. Just go to the server's tree,\nvisit the `jupytergraffiti_data` folder and click the folder download\nlink. For more details on how this works you can refer to \u003ca\nhref=\"https://github.com/data-8/nbzip\"\u003e`nbzip`'s\u003c/a\u003e documentation. \n\nYou can then commit the `jupytergraffiti_data` folder and your\nNotebook to your own github repo and set up a link to it on Binder to\nshare your Graffiti without requiring that the recipient have Graffiti\ninstalled.\n\n## Graffiti Software Installation-- Putting Graffiti onto your own System(s)\n\nUploading and downloading to/from Binder isn't optimal of course; it's probably better to get Graffiti going on your own computers.\n\nThere are four ways to install Jupyter Graffiti: using pip or conda, using a Python library, using a Docker image, or installing a plugin into your Jupyter Notebook server.\n\n### Installation Option #1: Use `pip` or `conda` (Easiest Option)\n\nJust do:\n\n `pip install jupytergraffiti`\n\nor\n\n`conda install -c willkessler jupytergraffiti`\n\nThis assumes you have `pip` or `pip3`, or `conda` as well as Jupyter Notebook, already installed.\n\n**PLEASE NOTE**: Due to changes in Jupyter, the inline terminals *will\nnot work* with any version of Jupyter less than 6.1.6. Please run:\n`conda update --prefix ~/anaconda3 anaconda` and `conda upgrade jupyter` to ensure that you have\nthe latest version, which at the time of the last update to this README is v6.2.0.\n\n### Installation Option #2: Run Jupyter Notebook with a Docker Image Containing Graffiti (Slightly More Complex Option)\n\nMake sure to **[install Docker](https://docs.docker.com/install)** on your system first (unless you've already installed it).\n\nThen enter this command in a terminal on your computer:\n\n```\n./jupytergraffiti/build_and_run.sh\n```\n\nThis will build and start up a Docker container running the Jupyter\nServer and the Jupyter Graffiti extension, with the container's home\ndirectory being mounted where your Jupyter Notebook(s) are located,\nand serving Notebooks over port 8888.\n\nThe advantage of using the Docker container is that *Jupyter Graffiti*\nis always loaded automatically, so you don't have to execute `import\njupytergraffiti` in the Notebook just to play back *Graffitis* (but\nyou *will* need to run it to access the _Graffiti_ API).\n\nusing the Docker container also ensures you're running a recent\nversion of Jupyter Notebook.\n\nTake a look at the output of the Jupyter Server running in the\ncontainer. It has the secret key you need to be able to surf to the Dockerized\nJupyter server. The output will look something like this (but note\nthat the Jupyter Server login token will change every time you run `build_and_run.sh`):\n\n```\nCopy/paste this URL into your browser when you connect for the first time,\n    to login with a token:\n        http://(b13ba2b482e9 or 127.0.0.1):8888/?token=e58a08f167881500e207ff9be05ad57ffe00e3457e54017c\n```\n\nWhat this is telling you is to surf to\n`http://localhost:8888?token=e58a08f167881500e207ff9be05ad57ffe00e3457e54017c`\nto access the Jupyter Server with the _Graffiti_ extension installed and\nrunning.\n\nThe Docker container will serve content out of port 8888. If you already have\nsomething (e.g. another Jupyter server) running on this port, pass a different port to\n`build_and_run.sh` like so:\n\n```\n./jupytergraffiti/build_and_run.sh 8889\n\n```\n\n**Note**: if you specify a different port, the Jupyter Server output\ncontaining the secret key will still show port 8888, because\ninternally it still uses port 8888; via Docker port mappings, we\nhave remapped 8888 to the port you specify. In the example above,\ntherefore, you would need to access the server at:\n`http://localhost:8889?token=e58a08f167881500e207ff9be05ad57ffe00e3457e54017c`.\n\n**Note**: Inline terminals do not seem to work within the Docker container setup, probably due to permission levels.\n\n### Installation Option #3: Use the Python Library (Slightly More Involved Option, but does not add code to your system)\n\n*Note*: Before using this method, you may need to Trust your\nNotebook. This is because Jupyter Graffiti is mostly written in Javascript,\nand by default, if the Notebook you're adding _Graffitis_ to was not\ncreated by you, Jupyter Notebook will not \"Trust\" it and will not run\nexternally loaded javascript code, for security reasons.  To Trust a\nNotebook, click `File...Trust Notebook`*before*  running the import\ncommand below.\n\n1. `git clone` this repo in the same directory where you keep the Notebook(s) you want to add _Graffiti_ to.\n1. Add and run the following command in a cell in the Notebook you want to start adding _Graffiti_ to:\n\n```\nimport jupytergraffiti\n```\n\nIf everything works, you will see a button labelled \"Activate Graffiti\" in your menu bar.\n\nIf you don't see this button appearing, use `Kernel... Restart and Clear Output` first, then try running ```import jupytergraffiti``` again.\n\n![kernel_restart](./images/tutorial/kernel_restart.png)\n\nOnce you see this message, you can \"Activate Graffiti\" on a Notebook\nto begin creating Graffiti. The \u003ca\nhref=\"user_manual/UserManual.ipynb\"\u003eUser Manual\u003ca\u003e has many details on\nhow to create Graffiti.\n\n**Special Note** : if you are adding *Graffitis* to Notebooks that do not reside in the same folder where you cloned this repo, then you must :\n\n1. Create a `jupytergraffiti_data` directory in the folder where you cloned this repo (`mkdir jupytergraffiti_data`).\n1. Create symbolic links from the directory where you Notebook resides\n   to both the `jupytergraffiti` folder in this repo, and to the\n   `jupytergraffiti_data` folder alongside where you cloned this repo.\n\n### Installation Option #4: Install the Graffiti Extension in Your Own Jupyter Server (Most Complex Option)\n\nThis will permanently install the extension in your computer's\ninstallation of the Jupyter Server (although you can always disable\nit if you want to). This means the extension will always be available\nwhenever you start up your Jupyter server. To install the extension:\n\n```\ncd jupytergraffiti\njupyter nbextension install graffiti_extension --symlink --user\njupyter nbextension enable graffiti_extension/main --user\ncd ..\n```\n\nYou may need to restart your Jupyter server to get the extension to load, although usually that's not required.\n\n#### Disabling the Graffiti extension in your Jupyter Server\n\nIf you need to disable the _Graffiti_ plugin for some reason, you can easily do it.\n\nTo disable the plugin:\n\n```\ncd jupytergraffiti\njupyter nbextension disable graffiti_extension/main --user\ncd ..\n```\n\nThen restart your Jupyter server.\n\n## Using the Jupyter Graffiti Python API\n\nWhen you `import jupytergraffiti` you get immediate access to\nfunctions you can use to control Jupyter Graffiti from Python. Some\nof these are utility functions, and others can be used to control\nrecordings playback.  To use them, simply run the Python functions in your\nNotebook's cells (note: the API only works with the Python kernels at this time).\n\n#### Playing Back Graffiti Recordings\n\nYou can also play any Graffiti recording back using Python\ncode. This could be valuable, for instance, after a student has failed\nseveral times to make some code work; you could watch for this\nsituation in your testing code, and offer to play a hint\nrecording. Or, if a student's code passes all tests you can start up a\nrecording prompting them to go on to the next exercise.\n\n## Current Limitations of Jupyter Graffiti\n\n* Jupyter Graffiti can record most activities in Notebooks, but it does not record interactions with Jupyter's UX, e.g. you will not see the Jupyter menus get pulled down even if you pulled down a Jupyter menu during a recording.\n* If you rearrange cells after making a recording, scrolling will try to align the cursor and the page as best it can with the cells you were mousing over and scrolling to, even if they are in a different order than when you made the original recording. However, due to complexities involving cell sizing, this process may not always be perfect.\n* Copying cells does not copy their Graffiti.\n* Make a Copy ... of the current Notebook will not create a copy of the recordings; in fact, it will use the same recording ID and therefore supplant recordings on the original Notebook. You can use an API call to fix this issue, however.\n* Given this is the first version of this software, there may well be bugs. Feel free to report issues on Github and/or propose PR's.\n\n## Future Plans\n\n* In the next version of *Jupyter Graffiti* you will be able to\n  automatically transcribe your spoken audio into subtitles that\n  scroll along with the movie.\n\n* Make a Copy ... of a Notebook should copy the recordings to a new Notebook recording ID.\n\n### CREDITS\n\nAuthor/Maintainer: \n\nWill Kessler \n\ngithub: \u003ca href=\"https://github.com/willkessler\"\u003ewillkessler\u003c/a\u003e\ntwitter: @atlas3650\n\n\nAdvisors: \n\n* Andy Brown (feature design)\n* Tugce Akin (engineering)\n* Nathan Tate (engineering).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillkessler%2Fjupytergraffiti","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwillkessler%2Fjupytergraffiti","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwillkessler%2Fjupytergraffiti/lists"}