• Stars
    star
    6,884
  • Rank 5,714 (Top 0.2 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 11 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

🎨 Classic MS Paint, REVIVED + ✨Extras

JS Paint

A pixel-perfect web-based MS Paint remake and more... Try it out!

JS Paint recreates every tool and menu of MS Paint, and even little-known features, to a high degree of fidelity.

It supports themes, additional file types, and accessibility features like Eye Gaze Mode and Speech Recognition.

Screenshot

Ah yes, good old Paint. Not the one with the ribbons or the new skeuomorphic one with the interface that can take up nearly half the screen. (And not the even newer Paint 3D.)

Windows 95, 98, and XP were the golden years of Paint. You had a tool box and a color box, a foreground color and a background color, and that was all you needed.

Things were simple.

But we want to undo more than three actions. We want to edit transparent images. We can't just keep using the old Paint.

So that's why I'm making JS Paint. I want to bring good old Paint into the modern era.

Current improvements include:

  • Open source (MIT licensed)
  • Cross-platform
  • Mobile friendly
    • Touch support: use two fingers to pan the view, and pinch to zoom
    • Click/tap the selected colors area to swap the foreground and background colors
    • View > Fullscreen to toggle fullscreen mode, nice for small screens
  • Web features
    • File > Load From URL... to open an image from the Web.
    • File > Upload to Imgur to upload the current image to Imgur.
    • Paste supports loading from URLs.
    • You can create links that will open an image from the Web in JS Paint. For example, this link will start with an isometric grid as a template: https://jspaint.app/#load:https://i.imgur.com/zJMrWwb.png
    • Rudimentary multi-user collaboration support. Start up a session at jspaint.app/#session:multi-user-test and send the link to your friends! It isn't seamless; actions by other users interrupt what you're doing, and visa versa. Sessions are not private, and you may lose your work at any time. If you want better collaboration support, follow the development of Mopaint.
  • Extras > Themes to change the look of the app. Dark mode included.
  • Eye Gaze Mode, for use with an eye tracker, head tracker, or other coarse input device, accessible from Extras > Eye Gaze Mode. With just a webcam, you can try it out with Enable Viacam (head tracker) or GazePointer (eye tracker).
  • Speech Recognition Mode. Using your voice you can select tools and colors, pan the view ("scroll down and to the left", or "go southwest", etc.), explore the menus (but you can activate any menu item without opening the menus first), interact with windows (including scrolling the history view with "scroll up"/"scroll down" etc.), dictate text with the Text tool, and even tell the application to sketch things (for instance, "draw a house")
  • Create an animated GIF from the current document history. Accessible from the Extras menu or with Ctrl+Shift+G. It's pretty nifty, you should try it out! You might want to limit the size of the image though.
  • Load and save many different palette formats with Colors > Get Colors and Colors > Save Colors. (I made a library for this: AnyPalette.js.)
    • You can also drag and drop palette files into the app to load.

Editing Features:

  • Use Alt+Mousewheel to zoom in and out
  • Edit transparent images! To create a transparent image, go to Image > Attributes... and select Transparent, then OK, and then Image > Clear Image or use the Eraser tool. Images with any translucent pixels will open in Transparent mode.
  • You can crop the image by making a selection while holding Ctrl
  • Keyboard shortcuts for rotation: Ctrl+. and Ctrl+, (< and >)
  • Rotate by any arbitrary angle in Image > Flip/Rotate
  • In Image > Stretch/Skew, you can stretch more than 500% at once
  • Zoom to an arbitrary scale in View > Zoom > Custom...
  • Zoom to fit the canvas within the window with View > Zoom > Zoom To Window
  • Non-contiguous fill: Replace a color in the entire image by holding Shift when using the fill tool

Miscellaneous Improvements:

  • Vertical Color Box mode, accessible from Extras > Vertical Color Box
  • You can use the Text tool at any zoom level (and it previews the exact pixels that will end up on the canvas).
  • Spellcheck is available in the textbox if your browser supports it.
  • Resize handles are easier to grab than in Windows 10's Paint.
  • Omits some Thumbnail view bugs, like the selection showing in the wrong place.
  • Unlimited undos/redos (as opposed to a measly 3 in Windows XP, or a measly 50 in Windows 7)
  • Undo history is nonlinear, which means if you undo and do something other than redo, the redos aren't discarded. Instead, a new branch is created in the history tree. Jump to any point in history with Edit > History or Ctrl+Shift+Y
  • Automatically keeps a backup of your image. Only one backup per image tho, which doesn't give you a lot of safety. Remember to save with File > Save or Ctrl+S! Manage backups with File > Manage Storage.

JS Paint drawing of JS Paint on a phone

Limitations:

A few things with the tools aren't done yet. See TODO.md

Full clipboard support in the web app requires a browser supporting the Async Clipboard API w/ Images, namely Chrome 76+ at the time of writing.

In other browsers you can still can copy with Ctrl+C, cut with Ctrl+X, and paste with Ctrl+V, but data copied from JS Paint can only be pasted into other instances of JS Paint. External images can be pasted in.

Supported File Formats

Image Formats

⚠️ Saving as JPEG will introduce artifacts that cause problems when using the Fill tool or transparent selections.

⚠️ Saving in some formats will reduce the number of colors in the image.

💡 Unlike in MS Paint, you can use Edit > Undo to revert color or quality reduction from saving. This doesn't undo saving the file, but allows you to then save in a different format with higher quality, using File > Save As.

💡 Saving as PNG is recommended as it gives small file sizes while retaining full quality.

File Extension Name Read Write Read Palette Write Palette
.png PNG 🔜
.bmp, .dib Monochrome Bitmap 🔜
.bmp, .dib 16 Color Bitmap 🔜
.bmp, .dib 256 Color Bitmap 🔜
.bmp, .dib 24-bit Bitmap N/A N/A
.tif, .tiff, .dng, .cr2, .nef TIFF (loads first page)
.pdf PDF (loads first page)
.webp WebP 🌐 🌐
.gif GIF 🌐 🌐
.jpeg, .jpg JPEG 🌐 🌐 N/A N/A
.svg SVG (only default size) 🌐
.ico ICO (only default size) 🌐

Capabilities marked with 🌐 are currently left up to the browser to support or not. If "Write" is marked with 🌐, the format will appear in the file type dropdown but may not work when you try to save. For opening files, see Wikipedia's browser image format support table for more information.

Capabilities marked with 🔜 are coming soon, and N/A of course means not applicable.

"Read Palette" refers to loading the colors into the Colors box automatically (from an indexed color image), and "Write Palette" refers to writing an indexed color image.

Color Palette Formats

With Colors > Save Colors and Colors > Get Colors you can save and load colors in many different formats, for compatibility with a wide range of programs.

If you want to add extensive palette support to another application, I've made this functionality available as a library: AnyPalette.js

File Extension Name Programs Read Write
.pal RIFF Palette MS Paint for Windows 95 and Windows NT 4.0
.gpl GIMP Palette Gimp, Inkscape, Krita, KolourPaint, Scribus, CinePaint, MyPaint
.aco Adobe Color Swatch Adobe Photoshop
.ase Adobe Swatch Exchange Adobe Photoshop, InDesign, and Illustrator
.txt Paint.NET Palette Paint.NET
.act Adobe Color Table Adobe Photoshop and Illustrator
.pal, .psppalette Paint Shop Pro Palette Paint Shop Pro (Jasc Software / Corel)
.hpl Homesite Palette Allaire Homesite / Macromedia ColdFusion
.cs ColorSchemer ColorSchemer Studio
.pal StarCraft Palette StarCraft
.wpe StarCraft Terrain Palette StarCraft
.sketchpalette Sketch Palette Sketch
.spl Skencil Palette Skencil (formerly called Sketch)
.soc StarOffice Colors StarOffice, OpenOffice, LibreOffice
.colors KolourPaint Color Collection KolourPaint
.colors Plasma Desktop Color Scheme KDE Plasma Desktop
.theme Windows Theme Windows Desktop
.themepack Windows Theme Windows Desktop
.css, .scss, .styl Cascading StyleSheets Web browsers / web pages
.html, .svg, .js any text files with CSS colors Web browsers / web pages

Did you know?

  • There's a black and white mode with patterns instead of colors in the palette, which you can get to from Image > Attributes...

  • You can drag the color box and tool box around if you grab them by the right place. You can even drag them out into little windows. You can dock the windows back to the side by double-clicking on their title bars.

  • In addition to the left-click foreground color and the right-click background color, there's a third color you can access by holding Ctrl while you draw. It starts out with no color so you'll need to hold Ctrl and select a color first. The fancy thing about this color slot is you can press and release Ctrl to switch colors while drawing.

  • You can apply image transformations like Flip/Rotate, Stretch/Skew or Invert (in the Image menu) either to the whole image or to a selection. Try scribbling with the Free-Form Select tool and then doing Image > Invert

  • These Tips and Tricks from a tutorial for MS Paint also work in JS Paint:

    • Brush Scaling (+ & - on the number pad to adjust brush size)
    • "Custom Brushes" (hold Shift and drag the selection to smear it)
    • The 'Stamp' "Tool" (hold Shift and click the selection to stamp it)
    • Image Scaling (+ & - on the number pad to scale the selection by factors of 2)
    • Color Replacement (right mouse button with Eraser to selectively replace the foreground color with the background color)
    • The Grid (Ctrl+G & Zoom to 4x+)
    • Quick Undo (Pressing a second mouse button cancels the action you were performing. I also made it redoable, in case you do it by accident!)
    • Scroll Wheel Bug (Hmm, let's maybe not recreate this?)

Desktop App

JS Paint can be installed as a PWA, altho it doesn't work offline.

(Also I built it into a desktop app with Electron and Electron Forge, but this will use unnecessary system resources and is not recommended. You can follow this issue for the first release.)

Development Setup

Clone the repo.

Install Node.js if you don't have it, then open up a command prompt / terminal in the project directory.

Testing

Run npm run lint to check for code problems.

Run npm test to run browser-based tests with Cypress. (It's slow to start up and run tests, unfortunately.)

Run npm run accept to accept any visual changes. This unfortunately re-runs all the tests, rather than accepting results of the previous test, so you could end up with different results than the previous test. If you use GitHub Desktop, you can view diffs of images, in four different modes.

To open the Cypress UI, first run npm run test:start-server, then concurrently npm run cy:open

Tests are also run in continuous integration with Travis CI.

Web App (https://jspaint.app)

After you've installed dependencies with npm i, use npm run dev to start a live-reloading server.

Make sure any layout-important styles go in layout.css. When updating layout.css, a right-to-left version of the stylesheet is generated, using RTLCSS.
You should test the RTL layout by changing the language to Arabic or Hebrew. Go to Extras > Language > العربية or עברית.
See Control Directives for how to control the RTL layout.

Desktop App (Electron)

This is basically ready for release, but as of yet unreleased.

  • Install dependencies with npm i
  • Start the electron app with npm run electron:start

electron-debug is included, so you can use F5/Ctrl+R to reload and F12/Ctrl+Shift+I to open the devtools.

You can build for production with npm run electron:make

Deployment

JS Paint can be deployed using a regular web server.

Nothing needs to be compiled.

CORS proxy

Optionally, you can set up a CORS Anywhere server, for loading images from the web, if you paste a URL into JS Paint, or use the #load:<URL> feature with images that are not on the same domain.

By default it will use a CORS Anywhere instance set up to work with jspaint.app.

It is hosted for free on Heroku, and you can set up your own instance and configure it to work with your own domain.

You'll have to find and replace https://jspaint-cors-proxy.herokuapp.com with your own instance URL.

Multiplayer Support

Multiplayer support currently relies on Firebase, which is not open source software.

You could create a Firebase Realtime Database instance and edit JS Paint's sessions.js to point to it, replacing the config passed to initializeApp with the config from the Firebase Console when you set up a Web App.

But the multiplayer mode is very shoddy so far. It should be replaced with something open source, more secure, more efficient, and more robust.

Embed in your website

Simple

Add this to your HTML:

<iframe src="https://jspaint.app" width="100%" height="100%"></iframe>

Start with an image

You can have it load an image from a URL by adding #load:<URL> to the URL.

<iframe src="https://jspaint.app#load:https://jspaint.app/favicon.ico" width="100%" height="100%"></iframe>

Advanced

If you want to control JS Paint, how it saves/loads files, or access the canvas directly, there is an unstable API.

First you need to clone the repo, so you can point an iframe to your local copy.

The local copy of JS Paint has to be hosted on the same web server as the containing page, or more specifically, it has to share the same origin.

Having a local copy also means things won't break any time the API changes.

If JS Paint is cloned to a folder called jspaint, which lives in the same folder as the page you want to embed it in, you can use this:

<iframe src="jspaint/index.html" id="jspaint-iframe" width="100%" height="100%"></iframe>

If it lives somewhere else, you may need to add ../ to the start of the path, to go up a level. For example, src="../../apps/jspaint/index.html". You can also use an absolute URL, like src="https://example.com/cool-apps/jspaint/index.html".

Changing how files are saved/loaded

You can override the file saving and opening dialogs with JS Paint's systemHooks API.

<script>
var iframe = document.getElementById('jspaint-iframe');
var jspaint = iframe.contentWindow;
// Wait for systemHooks object to exist (the iframe needs to load)
waitUntil(()=> jspaint.systemHooks, 500, ()=> {
	// Hook in
	jspaint.systemHooks.showSaveFileDialog = async ({ formats, defaultFileName, defaultPath, defaultFileFormatID, getBlob, savedCallbackUnreliable, dialogTitle }) => { ... };
	jspaint.systemHooks.showOpenFileDialog = async ({ formats }) => { ... };
	jspaint.systemHooks.writeBlobToHandle = async (save_file_handle, blob) => { ... };
	jspaint.systemHooks.readBlobFromHandle = async (file_handle) => { ... };
});
// General function to wait for a condition to be met, checking at regular intervals
function waitUntil(test, interval, callback) {
	if (test()) {
		callback();
	} else {
		setTimeout(waitUntil, interval, test, interval, callback);
	}
}
</script>

A Blob represents the contents of a file in memory.

A file handle is anything that can identify a file. You get to own this concept, and define how to identify files. It could be anything from an index into an array, to a Dropbox file ID, to an IPFS URL, to a file path. It can be any type, or maybe it needs to be a string, I forget.

Once you have a concept of a file handle, you can implement file pickers using the system hooks, and functions to read and write files.

Command Hooks Used
File > Save As systemHooks.showSaveFileDialog, then when a file is picked, systemHooks.writeBlobToHandle
File > Open systemHooks.showOpenFileDialog, then when a file is picked, systemHooks.readBlobFromHandle
File > Save systemHooks.writeBlobToHandle (or same as File > Save As if there's no file open yet)
Edit > Copy To systemHooks.showSaveFileDialog, then when a file is picked, systemHooks.writeBlobToHandle
Edit > Paste From systemHooks.showOpenFileDialog, then when a file is picked, systemHooks.readBlobFromHandle
File > Set As Wallpaper (Tiled) systemHooks.setWallpaperTiled if defined, else systemHooks.setWallpaperCentered if defined, else same as File > Save As
File > Set As Wallpaper (Centered) systemHooks.setWallpaperCentered if defined, else same as File > Save As
Extras > Render History As GIF Same as File > Save As
Colors > Save Colors Same as File > Save As
Colors > Get Colors Same as File > Open

Loading a file initially

To start the app with a file loaded for editing, wait for the app to load, then call systemHooks.readBlobFromHandle with a file handle, and tell the app to load that file blob.

const file_handle = "initial-file-to-load";
systemHooks.readBlobFromHandle(file_handle).then(file => {
	if (file) {
		contentWindow.open_from_file(file, file_handle);
	}
}, (error) => {
	// Note: in some cases, this handler may not be called, and instead an error message is shown by readBlobFromHandle directly.
	contentWindow.show_error_message(`Failed to open file ${file_handle}`, error);
});

This is clumsy, and in the future there may be a query string parameter to load an initial file by its handle. (Note to self: it will need to wait for your system hooks to be registered, somehow.)

There's already a query string parameter to load from a URL:

<iframe src="https://jspaint.app?load:SOME_URL_HERE"></iframe>

But this won't set up the file handle for saving.

Integrating Set as Wallpaper

You can define two functions to set the wallpaper, which will be used by File > Set As Wallpaper (Tiled) and File > Set As Wallpaper (Centered).

If you define only systemHooks.setWallpaperCentered, JS Paint will attempt to guess your screen's dimensions and tile the image, applying it by calling your systemHooks.setWallpaperCentered function.

If you don't specify systemHooks.setWallpaperCentered, JS Paint will default to saving a file (<original file name> wallpaper.png) using systemHooks.showSaveFileDialog and systemHooks.writeBlobToHandle.

Here's a full example supporting a persistent custom wallpaper as a background on the containing page:

const wallpaper = document.querySelector('body'); // or some other element

jspaint.systemHooks.setWallpaperCentered = (canvas) => {
	canvas.toBlob((blob) => {
		setDesktopWallpaper(blob, "no-repeat", true);
	});
};
jspaint.systemHooks.setWallpaperTiled = (canvas) => {
	canvas.toBlob((blob) => {
		setDesktopWallpaper(blob, "repeat", true);
	});
};

function setDesktopWallpaper(file, repeat, saveToLocalStorage) {
	const blob_url = URL.createObjectURL(file);
	wallpaper.style.backgroundImage = `url(${blob_url})`;
	wallpaper.style.backgroundRepeat = repeat;
	wallpaper.style.backgroundPosition = "center";
	wallpaper.style.backgroundSize = "auto";
	if (saveToLocalStorage) {
		const fileReader = new FileReader();
		fileReader.onload = () => {
			localStorage.setItem("wallpaper-data-url", fileReader.result);
			localStorage.setItem("wallpaper-repeat", repeat);
		};
		fileReader.onerror = () => {
			console.error("Error reading file (for setting wallpaper)", file);
		};
		fileReader.readAsDataURL(file);
	}
}

// Initialize the wallpaper from localStorage, if it exists
try {
	const wallpaper_data_url = localStorage.getItem("wallpaper-data-url");
	const wallpaper_repeat = localStorage.getItem("wallpaper-repeat");
	if (wallpaper_data_url) {
		fetch(wallpaper_data_url).then(response => response.blob()).then(file => {
			setDesktopWallpaper(file, wallpaper_repeat, false);
		});
	}
} catch (error) {
	console.error(error);
}

It's a little bit recursive, sorry; it could probably be done simpler. Like by just using data URLs. (Actually, I think I wanted to use blob URLs just so that it doesn't bloat the DOM inspector with a super long URL. Which is really a devtools UX bug. Maybe they've improved this?)

Specifying the canvas size

You can load a file that has the desired dimensions. There's no special API for this at the moment.

See Loading a file initially.

Specifying the theme

You could change the theme programmatically:

var iframe = document.getElementById('jspaint-iframe');
var jspaint = iframe.contentWindow;
jspaint.set_theme("modern.css");

but this will break the user preference.

The Extras > Themes menu will still work, but the preference won't persist when reloading the page.

In the future there may be a query string parameter to specify the default theme. You could also fork jspaint to change the default theme.

Specifying the language

Similar to the theme, you can try to change the language programmatically:

var iframe = document.getElementById('jspaint-iframe');
var jspaint = iframe.contentWindow;
jspaint.set_language("ar");

but this will actually ask the user to reload the application to change languages.

The Extras > Language menu will still work, but the user will be bothered to change the language every time they reload the page.

In the future there may be a query string parameter to specify the default language. You could also fork jspaint to change the default language.

Adding custom menus

Not supported yet. You could fork jspaint and add your own menus.

Accessing the canvas directly

With access to the canvas, you can implement a live preview of your drawing, for example updating a texture in a game engine in realtime.

var iframe = document.getElementById('jspaint-iframe');
// contentDocument here refers to the webpage loaded in the iframe, not the image document loaded in jspaint.
// We're just reaching inside the iframe to get the canvas.
var canvas = iframe.contentDocument.querySelector(".main-canvas");

It's recommended not to use this for loading a document, as it won't change the document title, or reset undo/redo history, among other things. Instead use open_from_file.

Performing custom actions

If you want to make buttons or other UI to do things to the document, you should (probably) make it undoable. It's very easy, just wrap your action in a call to undoable.

var iframe = document.getElementById('jspaint-iframe');
var jspaint = iframe.contentWindow;
var icon = new Image();
icon.src = "some-folder/some-image-15x11-pixels.png";
jspaint.undoable({
	name: "Seam Carve",
	icon: icon, // optional
}, function() {
	// do something to the canvas
});

async function systemHooks.showSaveFileDialog({ formats, defaultFileName, defaultPath, defaultFileFormatID, getBlob, savedCallbackUnreliable, dialogTitle })

Define this function to override the default save dialog. This is used both for saving images, as well as palette files, and animations.

Arguments:

  • formats: an array of objects representing types of files, with the following properties:
    • formatID: a string that uniquely identifies the format (may be the same as mimeType)
    • mimeType: the file format's designated media type, e.g. "image/png"
    • name: the file format's name, e.g. "WebP"
    • nameWithExtensions: the file format's name followed by a list of extensions, e.g. "TIFF (*.tif;*.tiff)"
    • extensions: an array of file extensions, excluding the dot, with the preferred extension first, e.g. ["bmp", "dib"]
  • defaultFileName: a suggested file name, e.g. "Untitled.png" or the name of an open document.
  • defaultPath (optional): a file handle for a document that was opened, so you can save to the same folder easily. Misnomer: this may not be a path, it depends on how you define file handles.
  • defaultFileFormatID: the formatID of a file format to select by default.
  • async function getBlob(formatID): a function you call to get a file in one of the supported formats. It takes a formatID and returns a Promise that resolves with a Blob representing the file contents to save.
  • function savedCallbackUnreliable({ newFileName, newFileFormatID, newFileHandle, newBlob }): a function you call when the user has saved the file. The newBlob should come from getBlob(newFileFormatID).
  • dialogTitle: a title for the save dialog.

Note the inversion of control here: JS Paint calls your systemHooks.showSaveFileDialog function, and then you calls JS Paint's getBlob function. Once getBlob resolves, you can call the savedCallbackUnreliable function which is defined by JS Paint. (Hopefully I can clarify this in the future.)

Also note that this function is responsible for saving the file, not just picking a save location. You may reuse your systemHooks.writeBlobToHandle function if it's helpful.

async function systemHooks.showOpenFileDialog({ formats })

Define this function to override the default open dialog. This is used for opening images and palettes.

Arguments:

  • formats: same as systemHooks.showSaveFileDialog

Note that this function is responsible for loading the contents of the file, not just picking a file. You may reuse your systemHooks.readBlobFromHandle function if it's helpful.

async function systemHooks.writeBlobToHandle(fileHandle, blob)

Define this function to tell JS Paint how to save a file.

Arguments:

  • fileHandle: a file handle, as defined by your system, representing the file to write to.
  • blob: a Blob representing the file contents to save.

async function systemHooks.readBlobFromHandle(fileHandle)

Define this function to tell JS Paint how to load a file.

Arguments:

  • fileHandle: a file handle, as defined by your system, representing the file to read from.

function systemHooks.setWallpaperTiled(canvas)

Define this function to tell JS Paint how to set the wallpaper. See Integrating Set as Wallpaper for an example.

Arguments:

  • canvas: a HTMLCanvasElement with the image to set as the wallpaper.

function systemHooks.setWallpaperCentered(canvas)

Define this function to tell JS Paint how to set the wallpaper. See Integrating Set as Wallpaper for an example.

Arguments:

  • canvas: a HTMLCanvasElement with the image to set as the wallpaper.

function undoable({ name, icon }, actionFunction)

Use this to make an action undoable.

This function takes a snapshot of the canvas, and some other state, and then calls the actionFunction function. It creates an entry in the history so it can be undone.

Arguments:

  • name: a name for the action, e.g. "Brush" or "Rotate Image 270°"
  • icon (optional): an Image to display in the History window. It is recommended to be 15x11 pixels.
  • actionFunction: a function that takes no arguments, and modifies the canvas.

function show_error_message(message, [error])

Use this to show an error message dialog box, optionally with expandable error details.

Arguments:

  • message: plain text to show in the dialog box.
  • error (optional): an Error object to show in the dialog box, collapsed by default in a "Details" expandable section.

function open_from_file(blob, source_file_handle)

Use this to load a file into the app.

Arguments:

  • blob: a Blob object representing the file to load.
  • source_file_handle: a corresponding file handle for the file, as defined by your system.

Sorry for the quirky API. The API is new, and parts of it have not been designed at all. This was just a hack that I came to depend on, reaching into the internals of JS Paint to load a file. I decided to document it as the first version of the API, since I'll want a changelog when upgrading my usage of it anyways.

function set_theme(theme_file_name)

Use this to change the look of the application.

Arguments:

  • theme_file_name: the name of the theme file to load, one of:
    • "classic.css": the Windows98 theme.
    • "dark.css": the Dark theme.
    • "modern.css": the Modern theme.
    • "winter.css": the festive Winter theme.
    • "occult.css": a Satanic theme.

function set_language(language_code)

You can kind of use this to change the language of the application. But actually it will show a prompt to the user to change the language, because the application needs to reload to apply the change. And if that dialog isn't in the right language, well, they'll probably be confused.

Arguments:

  • language_code: the language code to use, e.g. "en" for English, "zh" for Traditional Chinese, "zh-simplified" for Simplified Chinese, etc.

Changelog

The API will change a lot, but changes will be documented in the Changelog.

Not just a history of changes, but a migration/upgrading guide.

For general project news, click Extras > Project News in the app.

License

JS Paint is free and open source software, licensed under the permissive MIT license.

License GitHub Repo stars GitHub forks

More Repositories

1

98

💿 Web-based Windows 98 desktop recreation █████▓█▓▓▒▓▒▒░▒░░░🗕︎🗗︎🗙︎
JavaScript
1,161
star
2

textual-paint

🎨 MS Paint in your terminal.
Python
944
star
3

pipes

💿 Classic 3D Pipes screensaver remake (web-based)
JavaScript
362
star
4

os-gui

Retro OS GUI JS/CSS library
JavaScript
179
star
5

simple-console

Add an elegant command-line interface to any page
JavaScript
142
star
6

guitar

🎸 Online guitar toy and tablature recorder/player
JavaScript
94
star
7

mopaint

🎨🩸🔥🧞 Modern, modular painting program... (early stages)
JavaScript
92
star
8

wavey

An HTML5 online audio editor (BUGGY and not in development)
CoffeeScript
76
star
9

midi-recorder

🎹 The easiest way to record MIDI. No install. Automatically records.
JavaScript
76
star
10

anypalette.js

🎨 Read/write all color palette file formats ❤🧡💛💚💙💜
JavaScript
59
star
11

janitorial-android

Detailed LEGO Junkbot remake and fancy level editor
JavaScript
55
star
12

midiflip

🎹 MIDI music mayhem - flip, transpose, and arbitrarily remap pitches in MIDI files
JavaScript
41
star
13

node-ahk

Desktop scripting with Node.js based on IronAHK ⌨🖱🔥
C#
31
star
14

rezzy-zoom-and-enhance

🔎✨ Zoom and Enhance browser extension (AI powered)
JavaScript
28
star
15

tracky-mouse

Mouse control via head tracking, as a cross platform desktop app and JS library. eViacam alternative.
JavaScript
26
star
16

postcss-gtk

Processes GTK+ CSS into browser CSS
CoffeeScript
26
star
17

card-game-generator

🃏🎴 A tool for making/prototyping tabletop games
CoffeeScript
25
star
18

ascii-hypercube

Make ASCII art with HYPERRECTANGLES
JavaScript
23
star
19

true-random-movie

💫📽️ Pick a random movie from a VAST collection of titles (over 32K films)
JavaScript
17
star
20

slugg

🚃 A simple little up-going game
JavaScript
16
star
21

pbj-sandbox

2D point-based physics sandbox 🥜🍇🏖
JavaScript
16
star
22

retrores

A catalog of cursors and icons from Windows 98, in original and modern formats
JavaScript
16
star
23

elementary.css

elementary OS's stylesheet converted to browser CSS
CSS
16
star
24

tablature-parser

🎸 Parse guitar tabs (part of https://github.com/1j01/guitar)
CoffeeScript
15
star
25

she-has-what

⚪ Homestuck Doc Scratch text effect GIF generator
JavaScript
15
star
26

nonsensical

Generate grammatical sentences https://1j01.itch.io/nonsensical
JavaScript
14
star
27

diverge

🔱 explore textual possibilities like never before
JavaScript
14
star
28

skele2d

☠ A 2D game engine based around skeletal structures, with an in-game editor and animation support (pre-alpha)
CoffeeScript
13
star
29

nw-screensaver

🌌 Web based screensavers spanning multiple monitors, with transparency
CoffeeScript
13
star
30

tri-chromatic-keyboard

🎹 Better than a traditional piano layout
JavaScript
12
star
31

palettes

🎨 A collection of palette files of various types 💄🎃🍋🍏📗📘🔮
HTML
12
star
32

mos

Monochrome Operating System
JavaScript
12
star
33

pixelweaver

🎨🎲 Reproducible procedural drawing tool (pre-alpha)
JavaScript
11
star
34

bookmarklets

🔖 Fun functions, surreal when applied to websites you're familiar with
HTML
11
star
35

ascii-to-midi

Create MIDI files from text in several formats
JavaScript
10
star
36

sbahjifier

Make any page look like SWEET BRO AND HELLA JEFF
JavaScript
10
star
37

isaiahodhner.io

👨‍💻🌐🌻 My personal website, built with Next.js
JavaScript
7
star
38

suboptimal-research-tool

"Research" topics automatically
CoffeeScript
7
star
39

project-nexus

A hub for all your programming projects, and GUI for npm & package.json (not in development) - hey look a new thing!! https://webdash.xyz/ (solving the same sort of problem, and it looks nice!) ... ooh and another thing! https://github.com/720kb/ndm
JavaScript
7
star
40

scribble

DEPRECATED - Draws scribbly looking things. ➰✏
HTML
6
star
41

mind-map

🧠🗺 because your mind doesn't have ugly boxes everywhere
HTML
6
star
42

multifiddle

(development inactive) Fiddle with coffee, js, css, html, and all that, in a minimalistic collaborative environment
JavaScript
6
star
43

amg

DEPRECATED PROJECT IDEA - An abstracted modular game framework with integrated pixel editor and realtime asset manager and a level editor with dynamic block type definitions and a skeletal character animator and inverse kinematics and a game. Except no game.
JavaScript
6
star
44

oregano

Game about stripping oregano leaves. Peggle-like.
JavaScript
6
star
45

fliptimer

⏱ A countdown timer with a fun flipping animation ⏳
CoffeeScript
5
star
46

natter

💬 A chat app (not in active development)
JavaScript
5
star
47

process-tree

Node library to find all the child processes
CoffeeScript
5
star
48

tiamblia-game

🏹 An exploration adventure game
CoffeeScript
5
star
49

react-script

An elegant DSL for React - DEPRECATED: CoffeeScript 2 has JSX built in! http://coffeescript.org/v2/#jsx (requires a separate JSX→JS conversion)
CoffeeScript
5
star
50

pesterchum

Pesterchum Chat Client, and also a Doc Scratch lightning text effect reaction GIF generator
HTML
5
star
51

board-game

☖☗ A 2 player 3D board game of chance (and strategy)
JavaScript
5
star
52

psd-reader

(This was a fork but the original repo has moved off GitHub so it doesn't show up as one) Load Photoshop® PSD files in the browser
JavaScript
5
star
53

emoji-aquarium

Emoji Aquarium
Python
5
star
54

systemocracy

A card game in which you build a vast conspiracy to take over the world
CoffeeScript
5
star
55

chess-mashup

Chess game mashup in 3D
JavaScript
5
star
56

fish-game

A fishy board game. 🐟🐠 🦈
JavaScript
4
star
57

audio-sponge

🔉💦 a sound machine
CoffeeScript
4
star
58

techy-playing-cards

🂡 Very technical, you probably.. would understand...
CoffeeScript
4
star
59

tiamblia-original

🌳🐇 A little world experiment, precursor to Tiamblia
JavaScript
4
star
60

chiptool

(Project state: never mind) An experimental music making app (for chiptune, at least initially)
JavaScript
4
star
61

voxelite

Q: What if Minecraft but every pixel was a voxel? A: Worse performance, probably.
JavaScript
4
star
62

choon.js

🎹 Choon language interpreter in javascript with the Web Audio API.
JavaScript
3
star
63

rtttl.js

🎹 Parse and play RTTTL
JavaScript
3
star
64

polywogg

🐸⚔𐃉𑙪🗡𑙬🤺
TypeScript
3
star
65

ansi-art-thumbnailer

Thumbnail support for ANSI art files in file browsers
Python
3
star
66

ooplie

📜✍ Program in English
JavaScript
3
star
67

whitebread

🍞🌸 A surreal text adventure (early stages)
CoffeeScript
3
star
68

hacky-game

Crazy tech demo game thing with automagical LAN multiplayer
CoffeeScript
3
star
69

precorder

🎙 Record audio from the past ⌚⌛
CoffeeScript
3
star
70

alchemy

(not in development, not playable) click click click buy distill burn mix sell sell buy click drag sell wait sell wait sell
CoffeeScript
3
star
71

pool

🎱 A pool table simulation... where there's not enough friction
JavaScript
3
star
72

gif-maker

A gif making application.
CSS
3
star
73

laser

What if you could grab a laser? And swing around a laser emitter with it?
JavaScript
3
star
74

pipe-strip

🛋️👨📰🫳 | 🛋️👨🗞️💭❔ | 💬‼️🐱🚬
Python
3
star
75

organeq

🔢➗🔢 Plant a phantasmagorical mathematical syntax tree
CoffeeScript
2
star
76

une

🌌 3D starmap and misc tools for a roleplaying game
JavaScript
2
star
77

cityship

🚢{🏭🏢🏛}(🏗) Cityship Designer 2017
CoffeeScript
2
star
78

look-around-you.go

Learning Golang - and you can too!
Go
2
star
79

IDE

DEPRECATED PROJECT IDEA (also (knowingly) BROKEN) - an experimental IDE - Cloud9 is nice: https://c9.io/
JavaScript
2
star
80

ahk.js

AutoHotkey as a node module
C#
2
star
81

prosperity

A drafting game about building fantasy cities
JavaScript
2
star
82

1bpp

one bit per pixel game
C++
2
star
83

translate-great

Translate web app UI inline (just an experiment! less than a day's work!)
JavaScript
2
star
84

transpairency

Pair two screenshots together to make a transparent one.
JavaScript
2
star
85

boxart

drag and drop 3d box art creator
JavaScript
2
star
86

pruzzle

a jigsaw puzzle that's rigged — a rigsaw puzzle, if you will?
JavaScript
2
star
87

realistic-bird

#flappyjam game
CoffeeScript
2
star
88

delayed-casualty

⚔ Fighting game with simple controls (early stages of development)
CoffeeScript
2
star
89

nw-gtk

Psuedo-GTK in NW.js with React and postcss-gtk
2
star
90

window-switcher

Switch between windows of the same app, or between apps, on Windows
AutoHotkey
2
star
91

journal

✒ A programmer's journal app (with nice typography, and WYSIWYG) - I HAVEN'T TRIED USING THIS YET even tho i made it like most of the way to where it should be useable
CoffeeScript
1
star
92

hackathon

Fluid spatially structured chat concept
CoffeeScript
1
star
93

heroestube-the-game

DEPRECATED GAME PROJECT IDEA - A parody of YouTube Heroes as a game (The whole YouTube Heroes thing fell out of the news cycle and mindspace, so this isn't worth building anymore, but what I did implement is still kinda funny imo; there's nothing to it, no depth, but just the basic idea of it that you can see)
CSS
1
star
94

nxt-ultrasonic-mouser

Use the NXT's ultrasonic sensor to control the mouse and thus games and things.
Python
1
star
95

columns

A silly #badboxart jam platformer (complete game)
CoffeeScript
1
star
96

BuildGraph

DEPRECATED PROJECT IDEA - A visual node-graph-based cascading build system
CoffeeScript
1
star
97

dove-police

Dove Police
CoffeeScript
1
star
98

dat-boi

🐸 here comes dat boi
CoffeeScript
1
star
99

nw-synergy

An nw.js interface to Synergy
CoffeeScript
1
star
100

stick-mangler

A game with stick physics, called Mangy Stick or something
CoffeeScript
1
star