А можно примерно так:
У нас есть див с position: relative. Внутри него — канвас с position: absolute. Див ждет события mousedown, после чего по координатам мыши рисует в этой канве что-либо.
Далее кнопка отпущена, и при этом создается новый канвас, с position: absolute, поверх старого, и отрисовка производится уже на нем. Таким образом, у нас получается, что каждое действие находится в отдельном слое.
Когда количество слоев доходит до, скажем, 20, намый «нижний» из пачки объединяется с «пред-нижним». Для этого используем .drawImage() (
пример тут), который может отрисовывать графическое содержимое не только <img>, но и других <canvas>. Копируем содержимое «пред-нижнего» в «нижний», и после этого удалем «пред-нижний».
Как теперь делать отмену, думаю, понятно — убираем по очереди слои-канвасы сверху.
Вот так, дешево и сердито.