Giter VIP home page Giter VIP logo

org-heatmap's Introduction

org-heatmap

Introduction

Org-heatmap is an Emacs package for Org-mode users to keep track of their task consistency in a visual manner. It presents an overview heatmap on Org-mode agendas or Calendar, providing visual representation of task completions and task statistics.

Contents

Screenshots

Show habit completion overview in org agenda buffer.

images/screenshots/org-heatmap-habit-overview.gif

Display habit heatmap in Calendar

images/screenshots/org-heatmap-habit-calendar.gif

Add habit statistics after habits: (current streak, max streak and total done number).

images/screenshots/habit-statistics.png

Installation

You must also install these packages:
  • Emacs >= 28.2
  • emacsql >= 3.1.1
  • org-mode >= 9.6

ps. I have not tested in the lower versions of the mentioned packages.

Then put org-heatmap.el in your load-path, and eval:

 (use-package org-habit
	:custom
	(org-habit-graph-column 1)
	(org-habit-preceding-days 10)
	(org-habit-following-days 1)
	(org-habit-show-habits-only-for-today nil))

 (use-package org-heatmap
	:init
	(add-to-list 'load-path "/path-to/emacsql/")
	(add-to-list 'load-path "/path-to/org-heatmap/")
	(require 'org-heatmap)
	:after (org)
	:custom
	(org-agenda-files '("/path-to/org-heatmap/examples/examples.org"))
	(org-heatmap-db-location "/path-to/org-heatmap/examples/org-heatmap.db")
	:config
	(org-heatmap-mode))

Usage

Functions

Note: When you run these functions for the first time, you may experience some lag (the lag time depends on the length of your habits record).

  • org-heatmap-habit-draw-overview

    Draw an overview heatmap for the habit at point. When your cursor is on a habit in org-agenda-mode, you can call this function or press h to generate a heatmap for the habit. Call this function or press h again will erase the heatmap.

    You can hover your mouse over a rectangle, and you will see the time you spent on the habit on that day. Clicking the rectangle will redirect you to an agenda view of that day.

Note: This function uses org-heatmap-rectangle to draw heatmap, and for different fonts, the display effect may vary. The font used in the screenshots is Cascadia Mono. You can choose suitable characters based on the font you are using.

  • org-heatmap-calendar

    Display a three-month Gregorian calendar. Add highlights indicating the activities on the current calendar date.

    Whenever you complete a task (when a item is changed to DONE in org-mode.), the activity counter for the day will increase by one and update the database. You can use org-heatmap-calendar to generate a heatmap of your everyday activity, and use built-in functions such as org-calendar-goto-agenda to see details.

  • org-heatmap-habit-calendar

    Like org-heatmap-calendar, display a three-month Gregorian calendar for the habit at point.

    Add highlights indicating the times spent on the habit on the current calendar date.

    Whenever you complete a habit (when a item is changed to DONE in org-mode.), org-heatmap will record the time you spent on this habit today and write it to the database. You can use this command to generate a heatmap of your everyday time spent, and use built-in functions such as org-calendar-goto-agenda to see details.

  • org-heatmap-calendar-query

    In Calendar-mode with org-heatmap highlights, you can use this function (bound to f in calendar-mode) to get information about the activities on the current calendar date.

    When used with org-heatmap-calendar, it shows how many items are done on the current calendar date.

    When used with org-heatmap-habit-calendar, it shows how many times is spent on the habit on the current calendar date.

  • org-heatmap-adjust

    Change the number of done items on the current calendar date, used with org-heatmap-calendar (bound to j in calendar-mdoe).

    Note that this function is not applied to org-heatmap-habit-calendar for now.

  • org-heatmap-db--drop

    Delete a table from org-heatmap database.

Variables

  • org-heatmap-rectangle

    Characters used to draw overview heatmap.

    When your overview heatmap is not displayed ideally, you can consider changing to suitable characters.

  • org-heatmap-enable-habit-statics

    Whether to shoaw habit statics.

    Add three data after the habit entry: (current streak, max streak and total done number).

    Note: If you want to chage this variable, please set it before loading org-heatmap or use setopt.

  • org-heatmap-threshold

    Choose a different face based on the threshold arrived.

  • org-heatmap-db-location

    Default database location.

TODOs

  • [ ] Speed up database writes
  • [ ] Add project management
  • [ ] Record more data
  • [ ] Support for more complex database operations
  • [ ] Support for more data display modes

org-heatmap's People

Contributors

elilif avatar aspiers avatar

Stargazers

Roy avatar  avatar Daniel avatar tsohlacol avatar Kanogames avatar Daniel Ziltener avatar Zi LIANG avatar Celestial.y avatar yibie avatar joris avatar Aneeq Asif avatar Zygmunt avatar  avatar Antonio Romano avatar Lei Wang avatar yokirb avatar Narendra Joshi avatar Matt Duck avatar Mengguan Pan avatar p0tche avatar Dmitry Simonov avatar Sviatoslav Bulbakha avatar Laurynas Biveinis avatar a13ph avatar FCP avatar stardiviner avatar Masanori Ogino avatar  avatar Vlad Sirenko avatar Pank Su avatar  avatar Jacob Moena avatar Niklas Kunz avatar Filip Staffa avatar Matei Cotocel avatar  avatar  avatar Merghadi Abdelaziz avatar alan avatar StrawberryTea avatar 马川 avatar David Jankoski avatar Davide Restivo avatar DarkBuffalo avatar Kijima Daigo avatar Calum Sieppert avatar 🍃 Greg 'Krait' Hab 🍂 avatar Prashant Tak avatar  avatar Daniel Kraus avatar Abhinav Tushar avatar Patrick Delaney avatar  avatar  avatar Josh Mize avatar Zachary Romero avatar Jonas Bernoulli avatar Forge avatar  avatar Salih Muhammed avatar Sébastien Le Maguer avatar  avatar Ihor Radchenko avatar  avatar Bartlomiej Swiercz avatar  avatar  avatar  avatar Maikol Solís avatar  avatar Sylvain Soliman avatar Tu Do avatar Rafael Nicdao avatar Age avatar John Young avatar lijigang avatar Spike avatar di avatar  avatar Yu-Fu Fu avatar Qingshui Zheng avatar  avatar

Watchers

Laurynas Biveinis avatar  avatar  avatar Ihor Radchenko avatar  avatar  avatar Rahul Muthu avatar

Forkers

aspiers

org-heatmap's Issues

Cannot open load file: No such file or directory, sqlite

Seems like some kind of dependency issue. I loaded like this:

(use-package org-heatmap
  :straight (:host github :repo "Elilif/org-heatmap")
  :after (org)
  :config
  (org-heatmap-mode))

When I hit h on a habit in the agenda, I get:

Debugger entered--Lisp error: (file-missing "Cannot open load file" "No such file or directory" "sqlite")
  require(sqlite)
  (progn (require 'sqlite) (eieio-oset connection 'handle (sqlite-open (slot-value connection 'file))) (if emacsql-global-timeout (progn (emacsql connection [:pragma (= busy-timeout $s1)] (/ (* emacsql-global-timeout 1000) 2)))) (emacsql connection [:pragma (= foreign-keys on)]) (emacsql-register connection))
  (progn (progn (require 'sqlite) (eieio-oset connection 'handle (sqlite-open (slot-value connection 'file))) (if emacsql-global-timeout (progn (emacsql connection [:pragma (= busy-timeout $s1)] (/ (* emacsql-global-timeout 1000) 2)))) (emacsql connection [:pragma (= foreign-keys on)]) (emacsql-register connection)))
  (closure (t) (connection &rest _) "\n\n(fn CONNECTION &rest ##)" (progn (progn (require 'sqlite) (eieio-oset connection 'handle (sqlite-open (slot-value connection 'file))) (if emacsql-global-timeout (progn (emacsql connection [:pragma ...] (/ ... 2)))) (emacsql connection [:pragma (= foreign-keys on)]) (emacsql-register connection))))(#<emacsql-sqlite-builtin-connection emacsql-sqlite-builtin-connection-158017d8d448> (:file "~/.emacs.d/var/org/org-heatmap.db"))
  apply((closure (t) (connection &rest _) "\n\n(fn CONNECTION &rest ##)" (progn (progn (require 'sqlite) (eieio-oset connection 'handle (sqlite-open (slot-value connection 'file))) (if emacsql-global-timeout (progn (emacsql connection [:pragma ...] (/ ... 2)))) (emacsql connection [:pragma (= foreign-keys on)]) (emacsql-register connection)))) (#<emacsql-sqlite-builtin-connection emacsql-sqlite-builtin-connection-158017d8d448> (:file "~/.emacs.d/var/org/org-heatmap.db")))
  #f(compiled-function (&rest args) #<bytecode 0x1edac615e6e85cd3>)(#<emacsql-sqlite-builtin-connection emacsql-sqlite-builtin-connection-158017d8d448> (:file "~/.emacs.d/var/org/org-heatmap.db"))
  apply(#f(compiled-function (&rest args) #<bytecode 0x1edac615e6e85cd3>) #<emacsql-sqlite-builtin-connection emacsql-sqlite-builtin-connection-158017d8d448> (:file "~/.emacs.d/var/org/org-heatmap.db"))
  initialize-instance(#<emacsql-sqlite-builtin-connection emacsql-sqlite-builtin-connection-158017d8d448> (:file "~/.emacs.d/var/org/org-heatmap.db"))
  #f(compiled-function (class &rest slots) "Default constructor for CLASS `eieio-default-superclass'.\nSLOTS are the initialization slots used by `initialize-instance'.\nThis static method is called when an object is constructed.\nIt allocates the vector used to represent an EIEIO object, and then\ncalls `initialize-instance' on that object." #<bytecode -0x1b6fbe8e8f7d314b>)(emacsql-sqlite-builtin-connection :file "~/.emacs.d/var/org/org-heatmap.db")
  apply(#f(compiled-function (class &rest slots) "Default constructor for CLASS `eieio-default-superclass'.\nSLOTS are the initialization slots used by `initialize-instance'.\nThis static method is called when an object is constructed.\nIt allocates the vector used to represent an EIEIO object, and then\ncalls `initialize-instance' on that object." #<bytecode -0x1b6fbe8e8f7d314b>) emacsql-sqlite-builtin-connection (:file "~/.emacs.d/var/org/org-heatmap.db"))
  make-instance(emacsql-sqlite-builtin-connection :file "~/.emacs.d/var/org/org-heatmap.db")
  (let ((connection (make-instance #'emacsql-sqlite-builtin-connection :file file))) (if debug (progn (emacsql-enable-debugging connection))) connection)
  (progn (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq (car --cl-keys--) '(:debug :allow-other-keys)) (setq --cl-keys-- (cdr (cdr --cl-keys--)))) ((car (cdr (memq ... --cl-rest--))) (setq --cl-keys-- nil)) (t (error "Keyword argument %s not one of (:debug)" (car --cl-keys--)))))) (let ((connection (make-instance #'emacsql-sqlite-builtin-connection :file file))) (if debug (progn (emacsql-enable-debugging connection))) connection))
  (let* ((debug (car (cdr (plist-member --cl-rest-- ':debug))))) (progn (let ((--cl-keys-- --cl-rest--)) (while --cl-keys-- (cond ((memq (car --cl-keys--) '...) (setq --cl-keys-- (cdr ...))) ((car (cdr ...)) (setq --cl-keys-- nil)) (t (error "Keyword argument %s not one of (:debug)" (car --cl-keys--)))))) (let ((connection (make-instance #'emacsql-sqlite-builtin-connection :file file))) (if debug (progn (emacsql-enable-debugging connection))) connection)))
  emacsql-sqlite-builtin("~/.emacs.d/var/org/org-heatmap.db")
  (let ((conn (emacsql-sqlite-builtin org-heatmap-db-location))) (emacsql conn [:pragma (= foreign_keys ON)]) (let* ((process (and t (emacsql-process conn))) (s (and process (processp process)))) (if s (set-process-query-on-exit-flag process nil))) (if init-db (progn (org-heatmap-db--init conn 'done-items))) (setq org-heatmap--db conn))
  (let ((init-db (not (file-exists-p org-heatmap-db-location)))) (make-directory (file-name-directory org-heatmap-db-location) t) (let ((conn (emacsql-sqlite-builtin org-heatmap-db-location))) (emacsql conn [:pragma (= foreign_keys ON)]) (let* ((process (and t (emacsql-process conn))) (s (and process (processp process)))) (if s (set-process-query-on-exit-flag process nil))) (if init-db (progn (org-heatmap-db--init conn 'done-items))) (setq org-heatmap--db conn)))
  (if (and org-heatmap--db (emacsql-live-p org-heatmap--db)) nil (let ((init-db (not (file-exists-p org-heatmap-db-location)))) (make-directory (file-name-directory org-heatmap-db-location) t) (let ((conn (emacsql-sqlite-builtin org-heatmap-db-location))) (emacsql conn [:pragma (= foreign_keys ON)]) (let* ((process (and t (emacsql-process conn))) (s (and process (processp process)))) (if s (set-process-query-on-exit-flag process nil))) (if init-db (progn (org-heatmap-db--init conn 'done-items))) (setq org-heatmap--db conn))))
  org-heatmap-db()
  (let ((emacsql--connection (org-heatmap-db)) (emacsql--completed nil) (emacsql--transaction-level (1+ emacsql--transaction-level)) (emacsql--result)) (unwind-protect (while (not emacsql--completed) (condition-case nil (progn (if (= 1 emacsql--transaction-level) (progn (emacsql emacsql--connection ...))) (let ((result ...)) (setq emacsql--result result) (if (= 1 emacsql--transaction-level) (progn ...)) (setq emacsql--completed t))) (emacsql-locked (emacsql emacsql--connection [:rollback]) (sleep-for 0.05)))) (if (and (= 1 emacsql--transaction-level) (not emacsql--completed)) (progn (emacsql emacsql--connection [:rollback])))) emacsql--result)
  org-heatmap-db--table-exist-p("daily review")
  (if (org-heatmap-db--table-exist-p hd-name) nil (org-heatmap-habit--collect))
  (let ((inhibit-read-only t) (hd-name (org-heatmap--hd-name))) (if (or line-spacing (alist-get 'line-spacing default-frame-alist)) nil (set (make-local-variable 'line-spacing) 5)) (if (org-heatmap-db--table-exist-p hd-name) nil (org-heatmap-habit--collect)) (save-excursion (end-of-line) (insert "\n\n") (save-excursion (let* ((--cl-var-- 13)) (while (>= (setq --cl-var-- (1- --cl-var--)) 0) (insert (propertize (concat ... "\n") 'face 'org-heatmap-empty-rectangle))) nil)) (org-heatmap-add-color hd-name)) (put-text-property (point-min) (point-max) 'org-heatmap-has-overview-p (point)))
  (if (org-heatmap-habit-p) (let ((inhibit-read-only t) (hd-name (org-heatmap--hd-name))) (if (or line-spacing (alist-get 'line-spacing default-frame-alist)) nil (set (make-local-variable 'line-spacing) 5)) (if (org-heatmap-db--table-exist-p hd-name) nil (org-heatmap-habit--collect)) (save-excursion (end-of-line) (insert "\n\n") (save-excursion (let* ((--cl-var-- 13)) (while (>= (setq --cl-var-- ...) 0) (insert (propertize ... ... ...))) nil)) (org-heatmap-add-color hd-name)) (put-text-property (point-min) (point-max) 'org-heatmap-has-overview-p (point))) (user-error "Not on a habit!"))
  (if pos (progn (goto-char (point-min)) (org-agenda-redo) (goto-char pos)) (if (org-heatmap-habit-p) (let ((inhibit-read-only t) (hd-name (org-heatmap--hd-name))) (if (or line-spacing (alist-get 'line-spacing default-frame-alist)) nil (set (make-local-variable 'line-spacing) 5)) (if (org-heatmap-db--table-exist-p hd-name) nil (org-heatmap-habit--collect)) (save-excursion (end-of-line) (insert "\n\n") (save-excursion (let* ((--cl-var-- 13)) (while (>= ... 0) (insert ...)) nil)) (org-heatmap-add-color hd-name)) (put-text-property (point-min) (point-max) 'org-heatmap-has-overview-p (point))) (user-error "Not on a habit!")))
  (let* ((pos (and t (get-text-property (point) 'org-heatmap-has-overview-p)))) (if pos (progn (goto-char (point-min)) (org-agenda-redo) (goto-char pos)) (if (org-heatmap-habit-p) (let ((inhibit-read-only t) (hd-name (org-heatmap--hd-name))) (if (or line-spacing (alist-get 'line-spacing default-frame-alist)) nil (set (make-local-variable 'line-spacing) 5)) (if (org-heatmap-db--table-exist-p hd-name) nil (org-heatmap-habit--collect)) (save-excursion (end-of-line) (insert "\n\n") (save-excursion (let* (...) (while ... ...) nil)) (org-heatmap-add-color hd-name)) (put-text-property (point-min) (point-max) 'org-heatmap-has-overview-p (point))) (user-error "Not on a habit!"))))
  org-heatmap-habit-draw-overview()
  funcall-interactively(org-heatmap-habit-draw-overview)
  call-interactively(org-heatmap-habit-draw-overview nil nil)
  command-execute(org-heatmap-habit-draw-overview)

Provide use-package installation instructions

I heard that use-package will be officially included in 29.2 (IIRC). Even if this wasn't planned, I think it's well worth providing a snippet in the README for installation via this excellent macro.

Disabling the mode doesn't remove the function from `org-agenda-finalize-hook`

After experiencing #1 I reluctantly decided I had to disable org-heatmap to get a working agenda back. So I ran M-x org-heatmap-mode and it reported the mode was disabled. However after trying to refresh the agenda, it still failed with the error reported in #1. I looked at the stack trace and found that org-agenda-finalize-hook is still set to (org-heatmap-habit-add-streak org-modern-agenda). I think it should remove org-heatmap-habit-add-streak from org-agenda-finalize-hook when disabling the mode.

add a screenshot to README

A lot of people (including me) are in transit reading Reddit and find this package but have no way to try it out. It would be great to have a screenshot in the README so that I don't forget about it.

`forward-sexp: Search failed:` error

This package looks amazing! But unfortunately I couldn't get it to work: I get a forward-sexp: Search failed: error after enabling this mode and refreshing my agenda. Here's the stack trace:

  search-forward(#("NEXT [#A] catch up on email  :my:tags::" 0 1 (org-heading t fontified t isearch-open-invisible org-fold-core--isearch-show isearch-open-invisible-temporary org-fold-core--isearch-show-temporary org-fold--spec-org-fold-outline-1569524587413090725 org-fold-outline face (:inherit ((:background "#ff8059") org-modern-label)) display #(" N" 1 2 (cursor t)) effort nil effort-minutes nil) 1 3 (org-heading t fontified t isearch-open-invisible org-fold-core--isearch-show isearch-open-invisible-temporary org-fold-core--isearch-show-temporary org-fold--spec-org-fold-outline-1569524587413090725 org-fold-outline face (:inherit ((:background "#ff8059") org-modern-label)) effort nil effort-minutes nil) 3 4 (org-heading t fontified t isearch-open-invisible org-fold-core--isearch-show isearch-open-invisible-temporary org-fold-core--isearch-show-temporary org-fold--spec-org-fold-outline-1569524587413090725 org-fold-outline face (:inherit ((:background "#ff8059") org-modern-label)) display "T " effort nil effort-minutes nil) 4 5 (org-heading t fontified t isearch-open-invisible org-fold-core--isearch-show isearch-open-invisible-temporary org-fold-core--isearch-show-temporary org-fold--spec-org-fold-outline-1569524587413090725 org-fold-outline face org-level-3 effort nil effort-minutes nil) 5 6 (org-heading t fontified t isearch-open-invisible org-fold-core--isearch-show isearch-open-invisible-temporary org-fold-core--isearch-show-temporary font-lock-fontified t org-fold--spec-org-fold-outline-1569524587413090725 org-fold-outline face org-modern-priority display " " effort nil effort-minutes nil) 6 8 (org-heading t fontified t isearch-open-invisible org-fold-core--isearch-show isearch-open-invisible-temporary org-fold-core--isearch-show-temporary font-lock-fontified t org-fold--spec-org-fold-outline-1569524587413090725 org-fold-outline face org-modern-priority effort nil effort-minutes nil) 8 9 (org-heading t fontified t isearch-open-invisible org-fold-core--isearch-show isearch-open-invisible-temporary org-fold-core--isearch-show-temporary font-lock-fontified t org-fold--spec-org-fold-outline-1569524587413090725 org-fold-outline face org-modern-priority display " " effort nil effort-minutes nil) 9 30 (org-heading t fontified t isearch-open-invisible org-fold-core--isearch-show isearch-open-invisible-temporary org-fold-core--isearch-show-temporary org-fold--spec-org-fold-outline-1569524587413090725 org-fold-outline face org-level-3 effort nil effort-minutes nil) 30 33 (org-heading t) 33 42 (org-heading t inherited t) 42 43 (org-heading t) 43 47 (org-heading t inherited t) 47 49 (org-heading t)))
  (save-excursion (search-forward (get-text-property (point) 'txt)))
  (and habit (save-excursion (search-forward (get-text-property (point) 'txt))))
  (let* ((habit (and t (get-text-property (point) 'org-habit-p))) (pos (and habit (save-excursion (search-forward (get-text-property (point) 'txt))))) (ov (and pos (make-overlay (1- pos) pos)))) (if ov (overlay-put ov 'after-string (org-heatmap-habit-streaks habit)) nil))
  (while (not (eobp)) (let* ((habit (and t (get-text-property (point) 'org-habit-p))) (pos (and habit (save-excursion (search-forward (get-text-property ... ...))))) (ov (and pos (make-overlay (1- pos) pos)))) (if ov (overlay-put ov 'after-string (org-heatmap-habit-streaks habit)) nil)) (forward-line))
  (save-excursion (goto-char (point-min)) (while (not (eobp)) (let* ((habit (and t (get-text-property (point) 'org-habit-p))) (pos (and habit (save-excursion (search-forward ...)))) (ov (and pos (make-overlay (1- pos) pos)))) (if ov (overlay-put ov 'after-string (org-heatmap-habit-streaks habit)) nil)) (forward-line)))
  (let ((buffer-invisibility-spec '(org-link))) (save-excursion (goto-char (point-min)) (while (not (eobp)) (let* ((habit (and t (get-text-property ... ...))) (pos (and habit (save-excursion ...))) (ov (and pos (make-overlay ... pos)))) (if ov (overlay-put ov 'after-string (org-heatmap-habit-streaks habit)) nil)) (forward-line))))
  org-heatmap-habit-add-streak()
  org-agenda-finalize()
  #<subr F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_131>()
  funcall(#<subr F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_131>)
  (let ((org-agenda-compact-blocks 't) (org-agenda-skip-function '(closure (t) nil (and nil (org-agenda-skip-entry-if 'deadline 'scheduled))))) (funcall '#<subr F616e6f6e796d6f75732d6c616d626461_anonymous_lambda_131>))
  org-agenda-run-series("daily review" (((tags-todo "/NEXT|STARTED" ((org-agenda-overriding-header "Unscheduled #A TODOs") (org-agenda-skip-function (lambda nil (org-agenda-skip-entry-if ... "\\=.*\\[#A\\]" ...))))) (tags-todo "officehrs" ((org-agenda-overriding-header "Unscheduled [#AB] TODOs") (org-agenda-skip-function (lambda nil (org-agenda-skip-entry-if ... "\\=.*\\[#[AB]\\]" ...))))) (agenda "" ((org-agenda-ndays 3))) (tags-todo "/NEXT|STARTED" ((org-agenda-overriding-header "Unscheduled #B TODOs") (org-agenda-skip-function (lambda nil (org-agenda-skip-entry-if ... "\\=.*\\[#B\\]" ...)))))) ((org-agenda-compact-blocks t) (org-agenda-skip-function (lambda nil (and nil (org-agenda-skip-entry-if 'deadline 'scheduled)))))))
  org-agenda-redo(t)
  org-agenda-redo-all(nil)
  funcall-interactively(org-agenda-redo-all nil)
  call-interactively(org-agenda-redo-all nil nil)
  command-execute(org-agenda-redo-all)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.