forked from 1j01/jspaint
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
834 lines (806 loc) · 37.7 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JS Paint</title>
<!-- This should mirror CSP in electron-main.js, except maybe for firebase stuff. -->
<!-- Firebase stuff is somewhat speculative, as the quota is exceeded as I'm adding this. -->
<!-- Lax img-src is needed for speech recognition, e.g. interpret_command("draw a cat")[0].exec(); -->
<!-- connect-src needs data:/blob: for loading images via fetch, including from local storage. -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' https://jspaint.firebaseio.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
img-src 'self' data: blob: http: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src * data: blob: https://jspaint.firebaseio.com wss://jspaint.firebaseio.com;
">
<link href="styles/normalize.css" rel="stylesheet" type="text/css">
<link href="styles/layout.css" class="flippable-layout-stylesheet" rel="stylesheet" type="text/css">
<link href="styles/print.css" rel="stylesheet" type="text/css" media="print">
<link href="lib/os-gui/layout.css" class="flippable-layout-stylesheet" rel="stylesheet" type="text/css">
<!-- <link href="lib/os-gui/windows-98.css" rel="stylesheet" type="text/css"> -->
<!-- <link href="lib/os-gui/windows-default.css" rel="stylesheet" type="text/css" title="Windows Default"> -->
<!-- <link href="lib/os-gui/peggys-pastels.css" rel="alternate stylesheet" type="text/css" title="Peggy's Pastels"> -->
<!-- <link href="lib/tracky-mouse/tracky-mouse.css" rel="stylesheet" type="text/css"> -->
<!--
@TODO: bring these styles into OS-GUI.
This is a custom build of 98.css https://github.com/jdan/98.css
for checkboxes, radio buttons, sliders, and fieldsets,
excluding e.g. scrollbars, buttons, and windows (already in OS-GUI),
and integrating with the theme CSS vars used by OS-GUI,
and with some RTLCSS tweaks.
Text inputs and dropdowns are styled in classic.css, but should also be included in OS-GUI at some point.
This is not an @import in classic.css because it needs RTLCSS and I'm not applying RTLCSS to themes yet.
So I added .not-for-modern logic to theme.js to exclude these styles depending on the theme.
-->
<link href="lib/98.css/98.custom-build.css" class="flippable-layout-stylesheet not-for-modern" rel="stylesheet"
type="text/css">
<link rel="apple-touch-icon" href="images/icons/apple-icon-180x180.png">
<!-- Chrome will pick the largest image for some reason, instead of the most appropriate one. -->
<!-- <link rel="icon" type="image/png" sizes="192x192" href="images/icons/192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="images/icons/32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="images/icons/96x96.png"> -->
<!-- <link rel="icon" type="image/png" sizes="16x16" href="images/icons/16x16.png"> -->
<link rel="shortcut icon" href="favicon.ico">
<link rel="mask-icon" href="images/icons/safari-pinned-tab.svg" color="red">
<link rel="manifest" href="manifest.webmanifest">
<meta name="msapplication-TileColor" content="#008080">
<meta name="msapplication-TileImage" content="images/icons/ms-icon-144x144.png">
<meta name="theme-color" content="#000080">
<meta name="viewport" content="width=device-width, user-scalable=no">
<meta name="description" content="Classic MS Paint in the browser, with extra features" />
<meta property="og:image:width" content="279">
<meta property="og:image:height" content="279">
<meta property="og:description" content="Classic MS Paint in the browser, with extra features.">
<meta property="og:title" content="JS Paint">
<meta property="og:url" content="https://jspaint.app">
<meta property="og:image" content="https://jspaint.app/images/icons/og-image-279x279.jpg">
<meta name="twitter:title" content="JS Paint">
<meta name="twitter:description" content="Classic MS Paint in the browser, with extra features">
<meta name="twitter:image" content="https://jspaint.app/images/meta/twitter-card-plz-no-crop.png">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@isaiahodhner">
<meta name="twitter:creator" content="@isaiahodhner">
<script src="src/error-handling-basic.js"></script>
<script src="src/theme.js"></script>
</head>
<body>
<div id="about-paint" style="display: none">
<h1 id="about-paint-header">
<img src="images/icons/32x32.png" width="32" height="32" id="paint-32x32" alt="" />
<span id="jspaint-project-name">JS Paint</span>
<!-- <small id="jspaint-version" title="expect bugs!">Alpha</small> -->
<button id="view-project-news">What's New?</button>
</h1>
<div id="maybe-outdated-view-project-news" hidden>
<div id="maybe-outdated-line">
<div id="outdated" hidden>
<div class="on-official-host">
There's a new version of JS Paint. <a id="refresh-to-update" href=".">Refresh</a> to get it.
</div>
<div class="on-third-party-host">
This instance of JS Paint is outdated compared to <a href="https://jspaint.app"
target="_blank">jspaint.app</a>.
</div>
<div class="on-dev-host">
This version of JS Paint is outdated compared to <a href="https://jspaint.app"
target="_blank">jspaint.app</a>.
</div>
</div>
<div id="checking-for-updates" hidden>
Checking for updates...
</div>
<div id="failed-to-check-if-outdated" hidden>
Couldn't check for updates.
<span class="navigator-offline">You're offline.</span>
<span class="navigator-online">JS Paint may be outdated.</span>
</div>
</div>
</div>
<p>Open source under the permissive
<a href="https://github.com/1j01/jspaint/blob/master/LICENSE.txt">MIT License</a>.
</p>
<p>JS Paint is a web-based remake of MS Paint by <a href="https://isaiahodhner.io/">Isaiah Odhner</a>.</p>
<p>Read about the project and extra features on <a href="https://github.com/1j01/jspaint#readme">the readme</a>.
</p>
<p>Request features and report bugs <a href="https://github.com/1j01/jspaint/issues">on GitHub</a>
or <a href="mailto:[email protected]?subject=JS%20Paint">by email</a>.</p>
<p>Support the project at <a href="https://www.paypal.me/IsaiahOdhner"
target="_blank">paypal.me/IsaiahOdhner</a>.</p>
</div>
<script defer src="src/test-news.js"></script>
<!--
Before publishing a news update, make sure:
- The <time> element matches the date of the update.
- The id of the <article> will never need to be changed.
I'm using the format "news-YYYY-very-brief-description".
Avoiding putting the date in the id, in case I push the update later.
The id is used to check for updates, and is stored in localStorage.
- The console shows no errors.
test-news.js checks for some problems.
- HTML is valid.
-->
<div id="news" hidden>
<!-- <article class="news-entry" id="news-2021-saving">
<h1>The Savior Update</h1>
<time datetime=""></time>
<img width="" height="" style="max-width: 100%; height: auto; image-rendering: auto;" alt="" src="" />
<p>
<b>Save</b> (<kbd>Ctrl+S</kbd>) can now save over the open file, in Chrome, Edge, and Opera browsers.
This works using new <a target="_blank"
href="https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API">File System Access API</a>.
Always use <b>Save As</b> (<kbd>Ctrl+Shift+S</kbd>) if you want to save a new file.
</p>
<p>
<b>Save As</b> now asks for a file name and format.
PNG, GIF, and BMP are supported, including indexed color BMPs.
</p>
<p>
Tip: Use PNG if you don't have a specific reason to use another format,
as it has the best quality when saving.
</p>
<p>
If you open a BMP file with a palette, the palette is loaded into the Colors box.
If you load a monochrome BMP file, it loads a gradient of dither patterns into the Colors box.
</p>
<p>
<b>Black and White</b> mode in <b>Image > Attributes</b> is generalized to a monochrome mode
(although it's still called "Black and White" in the Attributes window.)
If an image has only two colors, when switching to "Black and White" mode,
it automatically adapts to these colors and gives you a gradient of dither patterns in the Colors box.
</p>
<p>
If you use <b>Image > Invert</b>, in monochrome mode, it flips the two colors in the image,
instead of changing colors to their RGB opposites.
(If the image is pure black and white, these two operations are equivalent.)
</p>
<p>
<b>Colors > Save Colors</b> also now asks for a file name and format.
An absurd number of file formats are supported.
You can even export CSS variables for use in a web design project.
RIFF Palette (*.pal) is compatible with MS Paint, and GIMP Palette (*.gpl) is compatible with many open source
graphics programs such as Inkscape and Krita.
</p>
<p>
You can find lots of palettes to use on <a target="_blank" href="https://lospec.com/palette-list">Lospec</a>.
Download a palette as GIMP GPL (or several other formats) and use <b>Colors > Get Colors</b> and select
the file, or simply drag and drop the file onto JS Paint to load the palette.
</p>
</article> -->
<article class="news-entry" id="news-2022-open-source">
<h1>Open Source</h1>
<time datetime="2022-07-28">2022-07-28</time>
<!-- <img width="" height="" style="max-width: 100%; height: auto; image-rendering: auto;" alt="" src="" /> -->
<p>
JS Paint is now finally open source, licensed under the
<a href="https://github.com/1j01/jspaint/blob/master/LICENSE.txt" target="_blank">MIT License</a>.
</p>
<p>
The project has been
<a href="https://en.wikipedia.org/wiki/Source-available_software" target="_blank">source-available</a>
from the beginning, since I never felt the need to obfuscate or hide the code,
but it is now legally open source.
</p>
<p>
I look forward to seeing how you use JS Paint in your own projects!
</p>
<p>
There is not yet a formal API for JS Paint,
but if you want to get in on the cutting edge,
you can take a look at how <a href="https://98.js.org" target="_blank">98.js.org</a>
embeds JS Paint.
Expect the API to change significantly in the future.
</p>
<p>
I've been meaning to open source JS Paint for a long time.
There are some legal issues, resources I don't have the copyright to,
but I think they should generally fall under fair use.
And I have created beautiful SVG versions of the icons,
so it's likely possible to have a version of JS Paint without any directly copyrighted resources.
But I've finally decided to stop worrying about it,
and just open source it already!
</p>
<p>
I hope you enjoy JS Paint!
</p>
</article>
<article class="news-entry" id="news-2021-saving">
<h1>The GUIcci Update</h1>
<time datetime="2021-12-08">2021-12-08</time>
<img width="640" height="360" style="max-width: 100%; height: auto; image-rendering: auto;" alt=""
src="https://i.postimg.cc/tgBncKfJ/guicii-update.png" />
<h2>New Features</h2>
<p>
<b>View > Zoom > Show Thumbnail</b> to show a preview of the image at a small size, great for pixel art.
Make fine, precise edits, while keeping it all in perspective.
</p>
<p>
<b>Pinch zooming:</b> If you have a touch screen, use two fingers to zoom in and out, and pan the view.
</p>
<p>
<b>Alt+Mousewheel</b> to zoom in and out quickly on desktop.
Unlike the Magnifier tool, this allows you to zoom while making (or moving) a selection, for added
precision.
</p>
<p>
Added <b>View > Fullscreen</b> to toggle fullscreen mode. This is nice for using JS Paint on your phone.
</p>
<p>
The <b>Text tool</b> now automatically expands the textbox as you type.
When resizing, there's now a minimum size based on the text in the textbox.
It previews exactly what size it will end up with when resizing.
</p>
<p>
<b>Docking:</b> If you drag the Colors box or Tools box out into a window,
you can now dock it back when dragging the titlebar.
Previously to dock it you had to double click the titlebar, or drag it by the edge of the window.
</p>
<img alt="Area that you have to click to drag a toolbar out into a window (in green)"
src="https://i.postimg.cc/7LB18Gcg/toolbar-drag-out-area.png" width="480" height="380"
style="max-width: 100%; height: auto; image-rendering: auto;" />
<p>
<b>Menus</b> are now fully keyboard (and screen reader) accessible.
In particular, you can hold <kbd>Alt</kbd> and press the access key of a menu button to open the menu,
and then (without <kbd>Alt</kbd>) press the access key of a menu item to select it.
The access key of an item is the underlined letter, or the first letter of the item's text if there's no
underline.
</p>
<p>
<b>Error details</b> are now hidden by default in error dialogs.
The details may be more overwhelming than useful in a lot of cases,
but if you need them, you can expand the details.
</p>
<img alt="Example error message box saying 'File not found', with details collapsed."
src="https://i.postimg.cc/ZR1qpVGw/file-not-found.png" width="408" height="145"
style="max-width: 100%; height: auto; image-rendering: auto;">
<p>
<b>File > Exit</b> now exits to the official web desktop,
<a target="_blank" href="https://98.js.org"><img
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABKklEQVR42qWTMW7DMAxFKYC+RC/hQR065xSdOyRDr9CxV8gQDzlK5g7RkLmn6CwCCimSchIoWkrAgESTj5+kHeCfFnpORCyPPiLqxgYP9gC/ZyKYEIG+CPAbn0JCr6okizmgvmdIF8BZxUhNgVdv8k3FvCdIu/u228VBnkiUGTZBzgyb1CcASBqdlhAMUEx6aAokWc8KqGCGzB+0lmMTyI0cBUmSV28zMRXzT4bECiI/l+NUZ/J0Cz0TACx6Tiwej9jfQkvIufZ8eVM1tM+1uiTHyP5P7H9IccvtxGCVSg0W6ZDUdz6cYbe8DgAHKKf3P9j8bnhQ0rSJY0DcOm2gwCFSugKqozT51V704xoDQItFWMeTFp+ZbQEGtm4o3/hsoLa1IaC3ocf/4QotsZxI//135gAAAABJRU5ErkJggg=="
width="16" height="16" alt=""> 98.js.org</a>,
a re-creation of Windows 98, full of games and applications.
</p>
<img class="inset-deep" src="https://i.postimg.cc/SKHrYpx3/98-js-org-screenshot.png"
alt="The 98.js desktop featuring desktop icons, a taskbar, and various application windows."
style="max-width: 100%; height: auto; image-rendering: auto;">
<p>
This project spun out of JS Paint, and I have implemented now
Sound Recorder, Notepad, Calculator, and even Windows Explorer,
to a high level of detail.
</p>
<p>
It also includes projects from other people, other recreations of old programs,
like <a target="_blank" href="https://webamp.org/">Webamp</a>,
a meticulous recreation of Winamp,
and <a target="_blank" href="https://github.com/rjanjic/js-solitaire">JS Solitaire</a>,
a Solitaire clone (I tweaked it for accuracy, adding the card back images, etc.)
</p>
<h2>Pixel Perfect</h2>
<p>
All interface elements are now thematically styled,
powered by <a target="_blank" href="https://os-gui.js.org">OS-GUI.js</a> and
<a target="_blank" href="https://jdan.github.io/98.css/">98.css</a>.
</p>
<p>
The whole interface is now pixel perfect accurate to Windows 98.
(Okay, there's a few things that are a pixel off or so, but seriously,
I lined up a screenshot and got it essentially perfect.)
</p>
<p>
Improved layout of <b>View > Zoom > Custom Zoom</b> window, matching the design in MS Paint.
</p>
<p>
Added padding to all dialogs so they don't feel cramped anymore.
</p>
<p>
Message boxes now include warning or error icons, and play a sound when they appear.
</p>
<p>
Improved <b>View > View Bitmap</b>: it now uses the theme's wallpaper background color,
if the image is smaller than the window.
It now closes with a click or key press, and doesn't let you edit the image (which was weird).
</p>
<p>
The Help window can now be minimized to the bottom of the screen, even though there's no taskbar.
It works like how Windows 98 does if the process managing the taskbar crashes.
</p>
<h2>Fixes</h2>
<p>
<b>Menu buttons</b> are easier to open on a touch screen. Sometimes you had to tap twice before the menu
opened.
</p>
<p>
Fixed <b>large square brush</b> continuity (it left gaps before, due to a half-implemented
optimization).
</p>
<p>
The <b>selection and textboxes</b> no longer "blow up" if you resize them to a minimal size.
They are now limited when you drag an edge past the opposite edge.
</p>
<p>
Fixed a bug where vertically thin selections were difficult or impossible to drag (despite showing a
drag cursor).
(The draggable region was offset outside of the selection box.)
Fixed a similar bug where tool previews would get offset if the canvas's height was very small.
</p>
<p>
Resize handles no longer get smaller when the object to resize is very small.
The draggable region for handles no longer gets smaller either, except in dimensions where it must.
It's now considerably smarter than Windows 10 about where it lets you drag handles from.
</p>
<p>
In <b>Image > Flip/Rotate</b>, you can now click the custom degrees input field before selecting the
"Rotate by angle" option.
</p>
<p>
The magnifier preview and other tool previews are now hidden while dragging the Colors box or Tools box.
It looked confusing when the magnifier preview was shown at the same time as
the preview outline for dragging/docking a tool window.
</p>
<p>
For languages that read <b>right-to-left</b>, the History view (<b>Edit > History</b>) now uses a
right-to-left layout,
and the color box and tool box no longer flip their layout when dragging them into a window or docking
them back to a side of the application.
</p>
<p>
The history view and error messages use <b>more localized text</b>.
</p>
<p>
Fixed <b>cut off icons</b> in buttons in the help window toolbar in the Modern theme.
</p>
<p>
All windows now have a default-focused control, and the last focused control in the window is remembered
for when you refocus the window.
</p>
<p>
<b>File > New</b> and <b>File > Open</b> now create a new autosave session,
instead of using the current session.
</p>
<h2>Winter Theme</h2>
<p>
Updated the Winter theme with advent calendar-style tool buttons,
that reveal (improved) holiday pixel art for each tool when you select them.
</p>
<p>
This means that the Winter theme is more usable,
since it doesn't obscure the functions of all the tools with pixel art.
</p>
<p>
Also, if it doesn't feel quite enough like an advent calendar for you,
you can hold <kbd>Shift</kbd> to select multiple tools at once.
</p>
<p>
Perhaps you could make a drawing using only one tool for the 16 days leading up to Christmas,
with exceptions for the Pick Color and Magnifier tools, of course.
</p>
<p>
Snowflakes in the menus indicate what letter you can press to select that item.
</p>
<p>
To disable the Winter theme, click the Grinch at the bottom of the screen,
who will then smile a nasty smile and steal Christmas from you.
You can get it back with <b>Extras > Theme > Winter</b>.
</p>
<img class="inset-deep" alt="Winter theme screenshot with candy canes and a text box saying Enjoy!"
src="https://i.postimg.cc/SxFtjy8z/winter-theme-enjoy.png" width="440" height="380"
style="max-width: 100%; height: auto; image-rendering: auto;" />
</article>
<article class="news-entry" id="news-2020-accessibility-update">
<h1>The Accessibility Update</h1>
<time datetime="2020-12-20">2020-12-20</time>
<img width="965" height="399" style="max-width: 100%; height: auto; image-rendering: auto;"
alt="Hello in several languages, eye gaze guiding a cursor, and a sea lion barking into a microphone."
src="https://i.postimg.cc/j29yrZbm/untitled-23.png" />
<h2>Multi-Lingual Support</h2>
<p>JS Paint is now largely localized into 26 languages.</p>
<p>
How am I releasing so many languages at the initial release of multi-lingual support, you may ask?
Well, this project has the somewhat unique opportunity to reuse localizations from an existing program,
since it's primarily a remake of MS Paint.
</p>
<p>
I downloaded and installed <a target="_blank" href="https://postimg.cc/4Y1V24wN">26 versions of Windows
98 in virtual machines</a>,
and extracted text from mspaint.exe in each one of them,
using a set of scripts that I wrote to to help me automate the process.
</p>
<p>
To change the language, go to <b>Extras > Language</b>.
Your preferred language may already be detected, if specified in system or browser settings.
</p>
<img width="1280" height="720" style="max-width: 100%; height: auto; image-rendering: auto;"
alt="26 languages, right off the bat!" src="https://i.postimg.cc/G2bH92fp/language-menu.png" />
<p>For Arabic and Hebrew, right-to-left layout is supported!</p>
<p>I tried my hand at some Arabic calligraphy...</p>
<img width="1141" height="800" style="max-width: 100%; height: auto; image-rendering: auto;"
alt="Calligraphy where the shapes of the tools in Paint make up Arabic words for them."
src="https://i.postimg.cc/NFX2TTp1/untitled-45.png" />
<p>
If you want to contribute translations, <a target="_blank"
href="https://github.com/1j01/jspaint/issues/80">get in touch!</a>
I need to do some technical work to set up for community translations on a public platform,
but I'm glad people have already expressed interest in helping translate!
(I also want to simplify the language in various parts of the UI before asking people to translate
them.)
</p>
<h2>Eye Gaze Mode</h2>
<img width="511" height="360" style="max-width: 100%; height: auto; image-rendering: auto;"
alt="You can use eye gaze or head movements to control the cursor."
src="https://i.postimg.cc/2yC137gc/20.png" />
<p>Eye Gaze Mode lets you control JS Paint without using your hands.</p>
<p>It's intended for use with an eye tracker, head tracker, or other coarse input scenario.</p>
<p>You don't need a thousand-dollar eye tracker device to play around with this, just a webcam and some free
software.</p>
<p>
I recommend <a target="_blank" href="https://eviacam.crea-si.com/">Enable Viacam</a>, which is
<em>not</em> an eye gaze tracker,
but rather a general video movement tracker that you can set up to track your head movement (or your
torso or hand or anything else).
</p>
<p>
Eye tracking via a webcam has a ways to go, but it's also pretty amazing in its own right.
Try <a target="_blank" href="https://sourceforge.net/projects/gazepointer/">GazePointer</a>.
</p>
<p>
Eye gaze tracking requires significant calibration, and if the calibration is off,
it's hard to use because you can't look where you want to look to interact with things.
This is why I recommend head tracking (if that's an option for you),
because then you can freely look around, and control the cursor <em>independently</em>,
so if it gets offset, you can just tilt your head a bit.
</p>
<p>
Eye Gaze Mode is built mainly for people with movement disabilities like ALS or Cerebral Palsy,
but it can also just be a sort of magical experience.
It can also be frustrating, and takes some practice to master.
</p>
<p>
A good place to start is coloring line art using just the Fill tool (<img src="help/p_paint.gif"
width="15" height="11" alt="">):
</p>
<ul>
<li>
<a target="_blank"
href="https://duckduckgo.com/?t=canonical&q=coloring+pages&atb=v232-1&iax=images&ia=images">Find
coloring pages online</a>,
and copy and paste them into JS Paint.
</li>
<li>
You can convert them to black and white in <b>Image > Attributes</b>, and then switch back to
Colors.
(This makes it work better with the Fill tool.)
</li>
<li>
Enable Eye Gaze Mode with <b>Extras > Eye Gaze Mode</b> and note that it will start clicking where
you hover.
You can disable this dwell clicking with the eye icon in the bottom of the screen.
</li>
<li>
Make the image fill the screen with <b>View > Zoom > Zoom To Window</b>.
</li>
</ul>
<p>
<span class="new">Bonus:</span> Since I implemented a vertical color box for Eye Gaze Mode,
I decided to make this available as a separate option. Access with <b>Extras > Vertical Color Box</b>.
</p>
<h2>Speech Recognition</h2>
<img width="549" height="275" style="max-width: 100%; height: auto; image-rendering: auto;"
alt="The sea lion says "Art! Art! Art!" into a microphone."
src="https://i.postimg.cc/BnQ8cpY2/Art-Art-Art.png" />
<p>
Using only your voice, you can switch tools and colors, pan the view, click on buttons on the screen by
name, and use most menu items.
You can even say "draw a cat in a party hat" to have JS Paint try to sketch a cat in a party hat.
</p>
<p>This feature pairs well with Eye Gaze Mode for a more complete hands free experience.</p>
<p>
The feature is only available on Chrome, and only understands English.
Note that Chrome sends your voice to Google servers.
</p>
<p>Access with <b>Extras > Speech Recognition</b>. If this option is grayed out, your browser is not
supported.</p>
<p>JS Paint will show what it thinks you said in the status bar at the bottom of the screen.</p>
<p>
There are many synonyms for commands, and often you can do things with very short phrases like "Curve"
to switch to the Curve tool.
If it's not recognizing your voice for short commands like "Curve" or "Cut", you may want to try longer
phrases like "Curve tool" or "Cut selection",
as this helps it distinguish the sound as speech, rather than a cough for instance.
</p>
<h2>Edit Colors Dialog</h2>
<p>I also implemented the Edit Colors dialog. Previously this used the native system color picker, and
didn't work for some people.</p>
<p>Access with <b>Colors > Edit Colors</b> or double click a color in the palette to edit.</p>
<figure>
<img width="496" height="350" style="max-width: 100%; height: auto; image-rendering: auto;"
src="https://i.postimg.cc/cLNgWH0r/Peek-2020-12-04-00-31.gif" />
<figcaption>An animation morphing between JS Paint and MS Paint's color picking dialog. It's pretty
close, other than the font.</figcaption>
</figure>
<p>Keyboard shortcuts are supported in this dialog, and for mobile devices with small screens, I made it
treat adding custom colors as a separate screen.</p>
<h2>Conclusion</h2>
<p>
JS Paint should be way more accessible now. And futuristic.
<p>
<p>
Of course there's always more that could be done.
Eye Gaze Mode could use brush stroke smoothing, and Speech Recognition could use Artificial General
Intelligence.
</p>
<p>
I'd love to see people using JS Paint, especially the Eye Gaze Mode and Speech Recognition,
so if you record a video of using JS Paint, please
<a target="_blank"
href="https://docs.google.com/forms/d/e/1FAIpQLSdGgS6TS4wBV89v8NoYHenh1eI8jYBfgwYBdPx-OaCEG5EW7g/viewform?usp=sf_link">send
it to me through this form.</a>
This lets me know what's actually important to people, and what's confusing,
and it gives me motivation to work on new features.
</p>
</article>
<article class="news-entry" id="news-2019-winter-update">
<h1>Winter Update</h1>
<time datetime="2019-12-20">2019-12-20</time>
<img width="563" height="334" style="max-width: 100%; height: auto; image-rendering: auto;" alt=""
src="https://i.postimg.cc/63Wc6vpG/2019-winter-update-candy-cane.gif" />
<h2>Winter Theme</h2>
<p>
A new UI skin is available, under <b>Extras > Themes > Winter</b>, featuring winter and holiday
icons, festive fonts, and a palette with seasonal colors and peppermint patterns.
</p>
<img width="256" height="16" alt="" src="images/winter/tools.png" />
<p>
Merry Christmas and happy Hanukkah!
</p>
<h2>Better History</h2>
<b class="new">New:</b> Jump to any point in the document's history, forwards or backwards, with <b>Edit
> History</b> or <kbd>Ctrl+Shift+Y</kbd>.
<ul>
<li>
Click on Text in the history view to go back to text editing.
</li>
<li>
You can return to when a selection existed.
</li>
<li>
Note: these states are skipped over with normal Undo and Redo, so you need to use the History
window.
</li>
<li>
Branching history: if you undo, and then make changes, you can get back to everything.
Future states are preserved.
</li>
</ul>
<b class="bad">Warning:</b> History is not saved with the autosave. Document history will be lost if you
refresh the page, or close the tab, or if the tab crashes, or if you close or restart your browser, or
likely if you're just on a phone and the mobile browser loses focus.
<h2>Improved Mobile Support</h2>
<p>
<b class="new">New:</b> Use two fingers to pan the view.
</p>
<p>
I recently made it easier to grab handles for resizing things.
With that, combined with multitouch panning,
JS Paint is much more useable on a phone.
</p>
<p>
<b class="bad">Caveat:</b> It's slow on some devices, and parts of the interface are still too small for
touch.
</p>
</article>
<article class="news-entry" id="news-2019-polygon-text-and-select">
<h1>Polygon, Text, and Select</h1>
<time datetime="2019-12-04">2019-12-04</time>
<p>
Handles are now way easier to drag, with extended click targets, similar to Paint from Windows 7.
It's not unreasonable to use with a touch screen now!
This applies to selections, textboxes, and the main canvas handles.
</p>
<p>
Resizing things while zoomed in is <a target="_blank"
href="https://github.com/1j01/jspaint/issues/13#issuecomment-562247085">finally fixed</a>!
</p>
<p>
The Text tool now perfectly previews the pixels that will be placed on the canvas.
What you see is what you get!
Also it retains all browser editing behavior, like spellcheck,
using a convoluted, yet elegant overlaying strategy.
(I prototyped this <a target="_blank" href="https://jsfiddle.net/1j01/wnac09u3/">here</a>
and <a target="_blank" href="https://jsfiddle.net/1j01/qkvfjn1r/">here</a> if you're interested.)
</p>
<p>
With the fill-only option selected, the Polygon tool now previews with inverted lines, like MS Paint
does.
(When you finish the polygon, the boundary of the shape matches the preview exactly,
because it actually <em>does</em> draw a stroke, just the same color as the fill.)
</p>
</article>
<article class="news-entry" id="news-2019-zoom-viewport">
<h1>Zoom To Mouse</h1>
<time datetime="2019-10-26">2019-10-26</time>
<p>
<b class="new">New:</b> The Magnifier now lets you zoom to a specific location,
showing a preview of the new viewport.
</p>
<p>
Also, when zooming out with the Magnifier,
or changing the zoom from the toolbar or menus,
the top left corner of the viewport is now kept anchored.
</p>
<p>
Also, pasting a selection will now go to the top left of the viewport,
instead of the entire document.
</p>
</article>
<article class="news-entry" id="news-2019-grid-zoom-cursors">
<h1>The Grid, Custom Zoom, and Dynamic Cursors</h1>
<time datetime="2019-10-09">2019-10-09</time>
<p>
<b class="new">New:</b> The Grid. Zoom to 4x+ and use <b>View > Zoom > Show Grid</b> or
<kbd>Ctrl+G</kbd> to enable.
This works with browser zoom as well to provide crisp gridlines even if you zoom in with your browser.
</p>
<p>
<b class="new">New:</b> <b>View > Zoom > Custom Zoom</b>,
including an actually-custom numerical zoom option, unlike MS Paint.
</p>
<p>
<b class="new">New:</b> Dynamic cursors for brush and eraser,
so you now have a preview of exactly where the tool will draw.
</p>
<p>
Also, in the event that your browser clears canvases to free up memory,
you should be more likely to be able to undo to get back to a useful state.
</p>
</article>
<article class="news-entry" id="news-2019-async-clipboard">
<h1>Full Clipboard Support</h1>
<time datetime="2019-09-21">2019-09-21</time>
<p>
JS Paint now lets you copy real image data to the Clipboard, both with keyboard shortcuts and from the
Edit menu.
This feature is available in Chrome 76+. Other browsers don't support it yet, as of Sep 2019.
</p>
<p>
Also: paste the URL of an image, and JS Paint will load and paste the image.
(This is an alternative to <b>File > Load from URL</b>, which replaces the document.)
</p>
</article>
<style>
#news {
background: white;
color: black;
}
.news-entry {
padding: 20px;
max-width: 563px;
}
.news-entry>h1 {
font-size: 1.3em;
margin: 0;
margin-bottom: 0.3em;
}
.news-entry>time {
font-size: 1.2em;
color: gray;
}
.news-entry>h2 {
font-size: 1.9em;
font-weight: normal;
margin: 0;
margin-bottom: 0.3em;
margin-top: 0.3em;
}
.news-entry .new {
color: green;
}
.news-entry .bad {
color: #d11a29;
}
</style>
</div>
<!-- Note: no CDNs, even with fallback, as the fallback is too complicated to handle with CSP. -->
<script src="lib/jquery-3.4.1.min.js"></script>
<script src="lib/gif.js/gif.js"></script>
<!-- pako is used by UPNG.js and UTIF.js -->
<script src="lib/pako-2.0.3.min.js"></script>
<script src="lib/UPNG.js"></script>
<script src="lib/UTIF.js"></script>
<script src="lib/bmp.js"></script>
<script src="lib/pdf.js/build/pdf.js"></script>
<script src="lib/anypalette-0.6.0.js"></script>
<script src="lib/FileSaver.js"></script>
<script src="lib/font-detective.js"></script>
<script src="lib/libtess.min.js"></script>
<!-- <script src="lib/tracky-mouse/tracky-mouse.js"></script> -->
<script src="lib/os-gui/parse-theme.js"></script>
<script src="lib/os-gui/$Window.js"></script>
<script src="lib/os-gui/MenuBar.js"></script>
<script src="lib/imagetracer_v1.2.5.js"></script>
<!-- must not be async/deferred, as it uses document.write(); and must come before other app code which uses localization functions -->
<script src="src/app-localization.js"></script>
<script src="src/msgbox.js"></script>
<script src="src/functions.js"></script>
<script src="src/helpers.js"></script>
<script src="src/storage.js"></script>
<script src="src/$Component.js"></script>
<script src="src/$ToolWindow.js"></script>
<!-- After show_error_message, showMessageBox, make_window_supporting_scale, and localize are defined,
set up better global error handling. -->
<!-- Note: This must be in the <body> as it also handles showing a message for Internet Explorer. -->
<script src="src/error-handling-enhanced.js"></script>
<script src="src/$ToolBox.js"></script>
<script src="src/$ColorBox.js"></script>
<script src="src/$FontBox.js"></script>
<script src="src/Handles.js"></script>
<script src="src/OnCanvasObject.js"></script>
<script src="src/OnCanvasSelection.js"></script>
<script src="src/OnCanvasTextBox.js"></script>
<script src="src/OnCanvasHelperLayer.js"></script>
<script src="src/image-manipulation.js"></script>
<script src="src/tool-options.js"></script>
<script src="src/tools.js"></script>
<!--<script src="src/extra-tools.js"></script>-->
<script src="src/edit-colors.js"></script>
<script src="src/manage-storage.js"></script>
<script src="src/imgur.js"></script>
<script src="src/help.js"></script>
<script src="src/simulate-random-gestures.js"></script>
<script src="src/menus.js"></script>
<script src="src/speech-recognition.js"></script>
<script src="src/app.js"></script>
<script src="src/sessions.js"></script>
<script src="lib/konami.js"></script>
<script src="src/vaporwave-fun.js"></script>
<noscript>
<h1><img src="images/icons/32x32.png" width="32" height="32" alt="" /> JS Paint</h1>
<p>This application requires JavaScript to run.</p>
<p>
Assuming this is the official instance of jspaint,
at <a href="https://jspaint.app">https://jspaint.app</a>,
you can safely enable JavaScript.
</p>
<p>You can also check out <a href="https://github.com/1j01/jspaint">the source code and project info</a>.</p>
</noscript>
<svg style="position: absolute; pointer-events: none; bottom: 100%;">
<defs>
<filter id="disabled-inset-filter" x="0" y="0" width="1px" height="1px">
<feColorMatrix in="SourceGraphic" type="matrix" values="
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
-1000 -1000 -1000 1 0
" result="black-parts-isolated" />
<feFlood result="shadow-color" flood-color="var(--ButtonShadow)" />
<feFlood result="hilight-color" flood-color="var(--ButtonHilight)" />
<feOffset in="black-parts-isolated" dx="1" dy="1" result="offset" />
<feComposite in="hilight-color" in2="offset" operator="in" result="hilight-colored-offset" />
<feComposite in="shadow-color" in2="black-parts-isolated" operator="in" result="shadow-colored" />
<feMerge>
<feMergeNode in="hilight-colored-offset" />
<feMergeNode in="shadow-colored" />
</feMerge>
</filter>
<filter id="disabled-inset-filter-2" x="0" y="0" width="1px" height="1px">
<feColorMatrix in="SourceGraphic" type="matrix" values="
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
-1 -1 -0 1 0
" result="black-and-blue-parts-isolated" />
<feFlood result="shadow-color" flood-color="var(--ButtonShadow)" />
<feFlood result="hilight-color" flood-color="var(--ButtonHilight)" />
<feOffset in="black-and-blue-parts-isolated" dx="1" dy="1" result="offset" />
<feComposite in="hilight-color" in2="offset" operator="in" result="hilight-colored-offset" />
<feComposite in="shadow-color" in2="black-and-blue-parts-isolated" operator="in"
result="shadow-colored" />
<feMerge>
<feMergeNode in="hilight-colored-offset" />
<feMergeNode in="shadow-colored" />
</feMerge>
</filter>
</defs>
</svg>
</body>
</html>