` + (this.folder? '▶' : '');
92 | }
93 |
94 | Move()
95 | {
96 | // set container program menu active
97 | this.programMenu.SetActive();
98 |
99 | // set active
100 | this.className = 'program programActive';
101 | activeProgram !=this && SystemSound(soundProgram, 0);
102 | activeProgram = this;
103 | }
104 |
105 | Open()
106 | {
107 | // hack: prevent user folders from opening in window
108 | if (this.folder && this.folder[0] && this.folder[0][4] & newUserProgram)
109 | return;
110 |
111 | if (this.window)
112 | {
113 | // set window to be active and clamp
114 | this.window.SetActive(1, 1);
115 | }
116 | else if (this.flags & newUserProgram)
117 | {
118 | // create user program with default code
119 | this.programMenu.NewUserProgram(undefined, this.userFolder);
120 | }
121 | else if (this.flags & deleteUserPrograms && confirm(this.name + '?'))
122 | {
123 | // close windows with matching folder
124 | [...desktop.children].map(child=> child.Close && (this.userFolder ?
125 | child.program.info.userFolder == this.userFolder : child.program.info.code != undefined) && child.Close());
126 |
127 | // remove user program infos
128 | programInfos = programInfos.filter(info=> info.code == undefined || this.userFolder && info.userFolder != this.userFolder);
129 |
130 | // rebuild menu and play sound
131 | RebuildMenu(OS13k.Save(SystemSound(soundClose, 4)));
132 | }
133 | else if (this.flags & closeAll)
134 | {
135 | // close all windows if no src or folder and play sound
136 | [...desktop.children].map(child=> child.Close && child.Close(1));
137 | SystemSound(soundClose);
138 |
139 | // reset window open position
140 | windowOpenX = startOpenOffset;
141 | windowOpenY = startOpenOffset + taskbarHeight;
142 |
143 | OS13k.Trophy('☕','OS13k','Coffee Is For Closers','Closed All');
144 | }
145 | else if (this.src || this.userProgram || this.folder)
146 | {
147 | // get saved window position
148 | let x = this.info.x, y = this.info.y;
149 |
150 | // update window open positions if no position was set
151 | x || (
152 | x = windowOpenX,
153 | y = windowOpenY,
154 | (windowOpenX += titlebarHeight) > 400 && (windowOpenX = windowOpenY = startOpenOffset),
155 | (windowOpenY += titlebarHeight) > 300 && (windowOpenY = windowOpenY = startOpenOffset + taskbarHeight));
156 |
157 | // open window
158 | this.window = new OS13kWindow(this, x, y);
159 |
160 | // update info and save
161 | this.Save();
162 | }
163 | }
164 |
165 | SetActive() { this.Open(); }
166 |
167 | Toggle() { activeWindow && activeWindow == this.window ? this.window.Close() : this.Open(); }
168 |
169 | Load()
170 | {
171 | if (this.folder)
172 | this.info = {};
173 | else
174 | {
175 | // load saved program info from local storage
176 | let i = programInfos.findIndex(e=> e.id == this.id);
177 | this.info = i < 0 ? {} : programInfos[i];
178 |
179 | // check for user code
180 | this.userProgram = this.info.code != undefined;
181 | }
182 | }
183 |
184 | // save program info and reset settings when closed if non sticky
185 | Save(open = 1)
186 | {
187 | // build save info
188 | this.info =
189 | {
190 | open,
191 | id: this.id,
192 | x: open | this.flags & sticky ? parseInt(this.window.style.left) : 0,
193 | y: parseInt(this.window.style.top),
194 | scale: open | this.flags & sticky ? this.window.scale : 1,
195 |
196 | // user program info
197 | name: this.name,
198 | icon: this.icon,
199 | width: this.width,
200 | height: this.height,
201 | code: this.info.code,
202 | allowSleep: this.userProgram? this.window.allowSleep.checked : 1,
203 | liveEdit: this.window.liveEdit.checked,
204 | userFolder: this.userFolder
205 | }
206 |
207 | // add to programs info and save
208 | let i = programInfos.findIndex(e=> e.id == this.id);
209 | OS13k.Save(i < 0 ? programInfos.push(this.info) : programInfos[i] = this.info);
210 | }
211 |
212 | } // OS13kProgram
213 | customElements.define('p-', OS13kProgram);
--------------------------------------------------------------------------------
/OS13k/OS13kProgramMenu.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | ///////////////////////////////////////////////////////////////////////////////
4 | // OS13kProgramMenu - holds a list of programs
5 |
6 | class OS13kProgramMenu extends HTMLElement
7 | {
8 | constructor(stubs, parentMenu)
9 | {
10 | super();
11 |
12 | // add to programs menu
13 | this.className = 'programMenu';
14 | this.parentMenu = parentMenu;
15 | this.programStubs = stubs;
16 | }
17 |
18 | Rebuild(y = 0)
19 | {
20 | // add programs to menu
21 | for(let stub of this.programStubs)
22 | {
23 | // create program and menu
24 | let program = stub[-1] = stub[-1] || new OS13kProgram(...stub);
25 | program.programMenu = program.folder ? new OS13kProgramMenu(program.folder, this) : this;
26 | }
27 |
28 | // clear programs menu
29 | this.innerHTML = '';
30 | programsMenu.appendChild(this);
31 |
32 | // add programs to menu
33 | this.programStubs.map(stub=> this.appendChild(stub[-1]));
34 |
35 | // set position
36 | this.style.top = y;
37 | this.style.left = this.parentMenu && this.parentMenu.getBoundingClientRect().right;
38 |
39 | // add folders after programs so width is correct
40 | this.programStubs.map(stub=>
41 | {
42 | // rebuild child program menus
43 | stub[-1].programMenu != this && stub[-1].programMenu.Rebuild(y);
44 |
45 | // add program height as we move down list
46 | y += programHeight;
47 | });
48 | }
49 |
50 | SetActive()
51 | {
52 | // close menus so they can reopen with this active
53 | CloseMenus();
54 |
55 | // set parent active
56 | this.parentMenu && this.parentMenu.SetActive();
57 |
58 | // make visible
59 | this.style.visibility = 'visible';
60 | }
61 |
62 | NewUserProgram(copyProgram, userFolder)
63 | {
64 | // create new program
65 | let stub = copyProgram ?
66 | [copyProgram.icon,, copyProgram.width, copyProgram.height, defaultFlags|code,
67 | copyProgram.name + '+', , ,++nextUserProgramId, copyProgram.userFolder] :
68 | ['✋',,,,defaultFlags|code,,,,++nextUserProgramId, userFolder],
69 | program = stub[-1] = new OS13kProgram(...stub);
70 |
71 | // add to menu program infos
72 | this.programStubs.push(stub);
73 |
74 | // set code, copy if passed in, use default if none found
75 | program.info.code = copyProgram ? (
76 | program.info.scale = copyProgram.info.scale,
77 | windowOpenX = copyProgram.info.x,
78 | windowOpenY = copyProgram.info.y + titlebarHeight,
79 | copyProgram.info.code)
80 | :
81 | '// Auto detects HTML, Dweet, or Shadertoy! You can drop files here too. ✌️😄\n' +
82 | 'for(x.fillRect(0,0,i=s=2e3,s);i--;x.globalAlpha=.1)\n' +
83 | 'x.clearRect((S(i)*1e9-t*i/9)%s,i*9%s,i%9,i%9)';
84 |
85 | // mark code as safe, open, and show code, prevent iframe focus so code can be focused
86 | program.Open(program.userProgram = allCodeIsSafe = 1);
87 | program.window.ShowCode(1);
88 | program.window.codeText.focus();
89 |
90 | // set menu and rebuild menus
91 | RebuildMenu(program.programMenu = this);
92 | return program;
93 | }
94 |
95 | } // OS13kProgramMenu
96 | customElements.define('m-', OS13kProgramMenu);
--------------------------------------------------------------------------------
/OS13k/OS13kTaskbarIcon.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | ///////////////////////////////////////////////////////////////////////////////
4 | // OS13kTaskbarIcon - icon on taskbar for opened programs
5 |
6 | class OS13kTaskbarIcon extends HTMLElement
7 | {
8 | constructor(program, windowOrMenu)
9 | {
10 | super();
11 |
12 | // create icon
13 | this.className = 'taskbarIcon';
14 | this.menu = this.windowOrMenu = windowOrMenu;
15 | this.SetName(this.program = program);
16 |
17 | // add to taskbar
18 | taskbarSpace.before(this);
19 | }
20 |
21 | SetName()
22 | {
23 | this.innerHTML = '
' + (this.program.icon || '💠');
24 | this.title = this.program.name;
25 | }
26 |
27 | Open() { this.SetActive(); }
28 |
29 | SetActive(active=1, clamp=1, focus=1)
30 | {
31 | // set window active and clamp
32 | active && this.windowOrMenu.SetActive(1, clamp, focus);
33 |
34 | // load icon cant be active taskbar item
35 | if (this == loadIcon)
36 | return SystemSound(soundMenu, .3);
37 |
38 | // set active style
39 | this.className = 'taskbarIcon ' + (active ? 'taskbarIconActive' : '');
40 |
41 | // if active, unselect old taskbar icon and set this active
42 | active && activeTaskbarIcon != this && (activeTaskbarIcon && activeTaskbarIcon.SetActive(0), activeTaskbarIcon = this);
43 | }
44 |
45 | } // OS13kTaskbarIcon
46 | customElements.define('i-', OS13kTaskbarIcon);
--------------------------------------------------------------------------------
/OS13k/OS13kTrayIcon.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | ///////////////////////////////////////////////////////////////////////////////
4 | // OS13kTrayIcon - icon on taskbar tray for OS shortcuts
5 |
6 | class OS13kTrayIcon extends HTMLElement
7 | {
8 | constructor()
9 | {
10 | super();
11 |
12 | // create tray icon and add it
13 | this.className = 'trayIcon';
14 | tray.appendChild(this);
15 | }
16 |
17 | SetProgram(program)
18 | {
19 | // set program, title, and icon
20 | this.program = program;
21 | this.title = program.name;
22 | this.innerHTML = program.icon;
23 | }
24 |
25 | Open() { this.program.Toggle(); }
26 | } // OS13kTrayIcon
27 | customElements.define('t-', OS13kTrayIcon);
--------------------------------------------------------------------------------
/OS13k/OS13kZzFX.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | ///////////////////////////////////////////////////////////////////////////////
4 | // ZzFXMicro - Zuper Zmall Zound Zynth
5 |
6 | // play a zzfx sound
7 | var zzfx = (...parameters)=> OS13k.PlaySamples(zzfxG(...parameters)),
8 |
9 | // generate zzfx samples
10 | zzfxG = (volume = 1, randomness = .05, frequency = 220, attack = 0, sustain = 0, release = .1, shape = 0, shapeCurve = 1, slide = 0, deltaSlide = 0, pitchJump = 0, pitchJumpTime = 0, repeatTime = 0, noise = 0, modulation = 0, bitCrush = 0, delay = 0, sustainVolume = 1, decay = 0, tremolo = 0, buffer = [])=>
11 | {
12 | attack = 99 + attack * defaultSampleRate;
13 | release = release * defaultSampleRate;
14 | sustain *= defaultSampleRate;
15 | decay *= defaultSampleRate;
16 | delay *= defaultSampleRate;
17 |
18 | // init parameters and helper functions
19 | let PI2 = Math.PI*2,
20 | sign = v=> v>0? 1 : -1,
21 | length = OS13k.randomSeed = OS13k.Clamp(attack + decay + sustain + release + delay, 9*defaultSampleRate) | 0,
22 | startSlide = slide *= 500 * PI2 / defaultSampleRate**2,
23 | startFrequency = frequency *= (1 + randomness*2*Math.random() - randomness) * PI2 / defaultSampleRate,
24 | modPhase = sign(modulation) * PI2/4,
25 | t=0, tm=0, i=0, j=1, r=0, c=0, s=0, f;
26 |
27 | repeatTime = repeatTime * defaultSampleRate | 0;
28 | pitchJumpTime *= defaultSampleRate;
29 | pitchJump *= PI2 / defaultSampleRate;
30 | deltaSlide *= 500 * PI2 / defaultSampleRate**3;
31 | for(modulation *= PI2 / defaultSampleRate;
32 |
33 | // loop and generate waveform, combine with buffer if passed in
34 | i < length; buffer[i] = (buffer[i++] || 0) + s)
35 | {
36 | if (!(++c%(bitCrush*100|0))) // bit crush
37 | {
38 | s = shape? shape>1? shape>2? shape>3? // wave shape
39 | Math.sin((t%PI2)**3) : // 4 noise
40 | Math.max(Math.min(Math.tan(t),1),-1): // 3 tan
41 | 1-(2*t/PI2%2+2)%2: // 2 saw
42 | 1-4*Math.abs(Math.round(t/PI2)-t/PI2): // 1 triangle
43 | Math.sin(t); // 0 sin
44 |
45 | s = (repeatTime ?
46 | 1 - tremolo + tremolo*Math.sin(2*Math.PI*i/repeatTime) // tremolo
47 | : 1) *
48 | sign(s)*(Math.abs(s)**shapeCurve) * // curve 0=square, 2=pointy
49 | volume * ( // envelope
50 | i < attack ? i/attack : // attack
51 | i < attack + decay ? // decay
52 | 1-((i-attack)/decay)*(1-sustainVolume) : // decay falloff
53 | i < attack + decay + sustain ? // sustain
54 | sustainVolume : // sustain volume
55 | i < length - delay ? // release
56 | (length - i - delay)/release * // release falloff
57 | sustainVolume : // release volume
58 | 0); // post release
59 |
60 | s = delay ? // delay
61 | s/2 + (delay > i ? 0 :
62 | (i pitchJumpTime) // pitch jump
73 | {
74 | frequency += pitchJump; // apply pitch jump
75 | startFrequency += pitchJump; // also apply to start
76 | j = 0; // reset pitch jump time
77 | }
78 |
79 | if (repeatTime && !(++r % repeatTime)) // repeat
80 | {
81 | frequency = startFrequency; // reset frequency
82 | slide = startSlide; // reset slide
83 | j = j || 1; // reset pitch jump time
84 | }
85 | }
86 |
87 | return buffer;
88 | },
89 |
90 | ///////////////////////////////////////////////////////////////////////////////
91 | //! ZzFXM (v2.0.2) | (C) Keith Clark & Frank Force | MIT | https://github.com/keithclark/ZzFXM
92 |
93 | zzfxM = (instruments, patterns, sequence, BPM = 125, validate) =>
94 | {
95 | let instrumentParameters, i, j, k, sample, patternChannel, isSequenceEnd,
96 | notFirstBeat, stop, instrument, pitch, attenuation, pan = 0,
97 | outSampleOffset, sampleOffset, nextSampleOffset, sampleBuffer = [],
98 | channelIndex = 0, hasMore = 1, channelBuffers = [[],[]],
99 | sampleCache = {}, beatLength = defaultSampleRate / BPM * 60 >> 2;
100 |
101 | // for each channel in order until there are no more
102 | for(; hasMore; channelIndex++)
103 | {
104 | // reset current values
105 | sampleBuffer = [hasMore = notFirstBeat = outSampleOffset = 0];
106 |
107 | // for each pattern in sequence
108 | sequence.map((patternIndex, sequenceIndex) =>
109 | {
110 | // get pattern for current channel, use empty 1 note pattern if none found
111 | patternChannel = patterns[patternIndex][channelIndex] || [0, 0, 0];
112 |
113 | // check if there are more channels
114 | hasMore |= !!patterns[patternIndex][channelIndex];
115 |
116 | // get next offset, use the length of first channel
117 | nextSampleOffset = outSampleOffset + (patterns[patternIndex][0].length - 2 - !notFirstBeat) * beatLength;
118 |
119 | // for each beat in pattern, plus one extra if end of sequence
120 | isSequenceEnd = sequenceIndex == sequence.length - 1;
121 | for (i = 2, k = outSampleOffset; i < patternChannel.length + isSequenceEnd; notFirstBeat = ++i)
122 | {
123 | // stop if end, different instrument, or new note
124 | stop = i == patternChannel.length + isSequenceEnd - 1 && isSequenceEnd ||
125 | instrument != (patternChannel[0] || 0) | patternChannel[i] | 0;
126 |
127 | // fill buffer with samples for previous beat, most cpu intensive part
128 | if (!validate)
129 | for(j = 0; j < beatLength && notFirstBeat;
130 |
131 | // fade off attenuation at end of beat if stopping note, prevents clicking
132 | j++ > beatLength - 99 && stop ? attenuation += (attenuation < 1) / 99 : 0
133 | )
134 | {
135 | // copy sample to stereo buffers with panning
136 | sample = (1-attenuation) * sampleBuffer[sampleOffset++] / 2 || 0;
137 | channelBuffers[0][k] = (channelBuffers[0][k] || 0) - sample * pan + sample;
138 | channelBuffers[1][k] = (channelBuffers[1][k++] || 0) + sample * pan + sample;
139 | }
140 |
141 | // set up for next note
142 | if (patternChannel[i])
143 | {
144 | // set attenuation and pan
145 | attenuation = patternChannel[i] % 1;
146 | pan = patternChannel[1] || 0;
147 |
148 | if (patternChannel[i] | 0)
149 | {
150 | // get cached sample
151 | sampleBuffer = sampleCache
152 | [[
153 | instrument = patternChannel[sampleOffset = 0] || 0,
154 | pitch = patternChannel[i] | 0
155 | ]] =
156 | sampleCache[[instrument, pitch]] ||
157 | (
158 | // add sample to cache
159 | instrumentParameters = [...instruments[instrument]],
160 | instrumentParameters[2] *= 2 ** ((pitch - 12) / 12),
161 | pitch > 0 ? zzfxG(...instrumentParameters) : []
162 | );
163 | }
164 | }
165 | }
166 |
167 | // update the sample offset
168 | outSampleOffset = nextSampleOffset;
169 | });
170 | }
171 |
172 | return channelBuffers;
173 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Welcome to OS13k!
2 | OS13k is a tiny operating system that fits in a 13 kb zip file.
3 |
4 | It includes native support for Shadertoys, Dweets, ZzFX sounds, music, trophies, touch input, and much more.
5 |
6 | # [Live Demo](https://killedbyapixel.github.io/OS13k) - [JS13k Submission](https://js13kgames.com/entries/os13k) - [Discord](https://discord.gg/n8vxUcZ)
7 |
8 | ## What is OS13k?
9 | - OS13k is a tiny web based pseudo OS and game engine designed for creative coding purposes
10 | - The core of OS13k is around 10k zipped including all the system apps
11 | - OS13k can connect with other JS13k games via local storage to add music and trophies
12 | - Users can extend OS13k by addinng their own programs and shaders
13 |
14 | ## Features
15 | - ZzFX sound effects with support for sound seeds
16 | - ZzFXM music system, player, and visualizer
17 | - Trophy system and viewer
18 | - Centralized input system
19 | - Custom user programs
20 | - Dwitter, ShaderToy, and WebGL support
21 | - GUI with window manger, taskbar, tray and settings
22 | - Mobile/touch support
23 |
24 | ## Programming Info
25 | - OS13k stores it's list of programs in programs.js
26 | - For fast iteration when developing, most recent active window is opened on startup
27 |
28 | Add an icon config to programs.js to register your program, examples...
29 | - [icon, src, width, height, flags, name, help, folder]
30 | - ['?','help.html']
31 | - ['✌️😄','system/systemTest.html',,,full|resize|code|sticky]
32 | - ['🌊','dweets/underwaterCavern.dweet.js']
33 | - ['☯','toys/infiniteYinYangs.shader.txt',500,500,full,'Put instructions here.']
34 |
35 | ### Programs
36 | - OS13k can open any html file and it will work the same as if opened directly
37 | - Chrome is recommended, but Firefox is also supported
38 | - [Viewing OS13k locally may not work if it treats local files as cross-origin](https://discourse.mozilla.org/t/firefox-68-local-files-now-treated-as-cross-origin-1558299/42493/9)
39 | - Prefix all local storage keys with OS13kYourProgramName to prevent collisions during JS13k (use at least 2 letters)
40 | - When the reload button is clicked, OS13kReload is called if it exists instead of reloading the iframe
41 | - For development we recommend [VSCode](https://code.visualstudio.com/) with the [Live Server Plugin](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)
42 | - You can also create a custom program to edit code directly in OS13k
43 |
44 | ### Trophies
45 | - Trophies are perhaps the most important part of OS13k and have many uses
46 | - Apps can register trophies for their games, the OS tracks which are unlocked
47 | - To unlock trophies use OS13k.Trophy(icon, gameName, trophyName, message)
48 | - You can pass in a value as the message, like a high score for example
49 | - *HTML tags and commas can not be used in trophy data*
50 | - When a new trophy is unlocked or the message is changed a popup will automatically appear
51 | - Total trophy count is shown in the taskbar and the trophy case shows all unlocked trophies
52 | - *You can use tophies to store data!* Use OS13k.GetTrophy to get a trophy message
53 |
54 | ### Trophy Functions
55 | - OS13k.Trophy(game='', icon='', name='', message='') - Unlock a trophy
56 | - OS13k.GetTrophy(game, name) - Get most recent matching trophy, 0 if no trophy
57 | - OS13k.Trophies() - Get full list of trophy objects
58 |
59 | ### Sound
60 | - ZzFx sounds are supported by default and several other audio functions are provided
61 | - ZzFX is open source sound effect generator with an easy to use sound designer https://zzfx.3d2k.com/
62 | - A seeded ZzFX sound player is available to save space with much smaller sound calls
63 | - OS13k.PlaySeed(seed, lengthScale=1, volume=1, randomness=.05, frequency) - Play a zzfx sound from seed
64 | - OS13k.PlaySamples(samples, sampleRate=44100) - Play audio samples
65 | - OS13k.Note(semitoneOffset=0, rootNoteFrequency=440) - Get frequency of a note on a musical scale
66 | - OS13k.Speak(text, language='en', stopSpeech, volume=1, rate=1, pitch=1) - Play speech of the text
67 | - Seeds can also be strings (will be hashed) or full ZzFX sounds
68 | - A custom gain node is created for every sound, use sound.gain.gain.value to change
69 |
70 | ### Music
71 | - [ZzFXM by Keith Clark](https://github.com/keithclark/ZzFXM) is the music player
72 | - OS13k.PlayMusic(song) - plays the song with zzfxm
73 | - OS13k.GetAnalyser() - returns a 32x32 music analyser canvas
74 | - OS13k.GetAnalyserData(i) - returns index into a 32 length array of frequency volumes normalized between 0-1
75 | - OS13k.StringToMusic(string, validate) - Converts a string to a music array and checks if valid
76 |
77 | ### System Calls
78 | - The OS13k object is set in your program after load, if you need it on load use parent.OS13k
79 | - zzfx also becomes available after your program loads and can be called directly
80 | - OS13k.CreateShader(canvas, shaderCode) - Create a shadertoy compatible webgl shader
81 | - OS13k.RenderShader(canvas, shaderProgram, time=0, frame=0) - Render a shader
82 | - OS13k.StripHTML(string) - Removes all HTML tags in a string
83 | - OS13k.Hash(string) - Returns numeric hash code for a string
84 | - OS13k.Popup(html, speak) - Shows a popup with html body and optional speech and sound
85 |
86 | ### Math Library
87 | - OS13k.Random(max=1, min=0) - Get a seeded random value clamped between min and max
88 | - OS13k.randomSeed - You must set the seed before calling OS13k.Random
89 | - OS13k.Clamp(a, max=1, min=0) - Clamp value between max and min
90 | - OS13k.Percent(v, a, b) - Get clamped percent between a and b
91 | - OS13k.Lerp(p, a, b) - Lerp clamped percent between a and b
92 |
93 | ### Dweets and Shadertoys
94 | - Programs with the extension .dweet.js or .shader.txt or will automatically load as Dweets or Shadertoys!
95 | - Dweets and Shadertoys are automatically paused when they don't have focus (after a 1 second warmup)
96 | - They also automatically have the show code option by default unless explictly disabled
97 | - Dweets can do anything that other programs can do including calling OS13k functions and ZzFX
98 | - Dweets and Shadertoys are automatically paused when not focused (unless awake is set)
99 | - Shaders support iTime, iFrame, iMouse, iResolution, and iChannel0
100 | - iChannel0 is an image of the previous frame, this can be used to make effects or store game logic
101 |
102 | ### Input System
103 | - OS13k provides an easy to use input system to help eliminate redundant code
104 | - Call OS13k.Input(window) to get the input object
105 | - the object format is {x, y, keypress, keydown, mousex, mousey, mousepress, mousedown}
106 | - x and y is a -1 to 1 direction from WASD or direction buttons
107 | - mousex and mousey is the mouse position
108 | - wheel is the mouse wheel delta
109 | - keypress and mousepress are arrays, an element is 1 if that key is pressed
110 | - keydown and mousedown are arrays, an element is 1 if that key is down
111 | - *See System/Test/InputTest for an example*
112 |
113 | ### Program Settings and Defaults
114 | - name - Display name (if absent will build nice name from camel case src filename)
115 | - src - Source filename
116 | - icon - Can contain html tags, fits about 2 emojis
117 | - don't close html tags, they will automatically be closed
118 | - width (720) and height (405) - Size of window (default is 16:9 aspect)
119 | - help (optional) - Help message, shows an icon on the window's titlebar (try to keep it short)
120 | - author (optional) - Name of creator
121 | - sticky (0) - Will automatically open of program on restart if it was open
122 | - reload (1) - Shows the reload option
123 | - awake (1) - Prevents window dim and and pausing dweets/shaders when not focused
124 | - full (1) - Enables full screen option
125 | - code (0) - Shows code option, defaults to true for dweets/shaders, help is shown instead if it exists
126 | - rezize (1) - Allows resizing the window
127 | - shortcut (0) - Shows shortcut icon on the desktop
128 |
129 | ### User Programs
130 | - You can create and access custom programs in the user programs folder
131 | - *User programs have the same capabilities as any other program!*
132 | - It auto detects HTML (starts with <), Shadertoy (has void mainImage), or Dweet
133 | - This can be used to iterate on dweets or small shaders, or to load a full program.
134 | - Drag and drop a file into the text box to load it
135 | - The screenshot button is available for Dweets and Shadertoys
136 | - User Dweets has loop protection to help prevent freeze ups, though it can still occur
137 | - Press Alt+Enter to reload when live edit is disabled
138 | - User programs will not run until clicked to prevent executing bad code
139 |
140 | ### Any JS13k game can add trophies and music, even if not part of OS13k!
141 | - *To add a trophy or music track, just save a special key to localStorage!*
142 | - The smallest way to add a single trophy (like for winning) is localStorage['OS13kTrophy,Icon,GameName'] = ''
143 | - For more control use localStorage['OS13kTrophy,Icon,Game Name,Trophy Name'] = Message
144 | - You can change the message to update the trophy, like a highscore for example
145 | - Music works the same way, use localStorage['OS13kMusic,Song Name'] = JSON.stringify(song)
146 | - OS13k automatically checks localStorage and display popups for new trophies and songs from other games
147 | - This is possible because all JS13k games share the same local storage! Pretty cool right?
148 | - You can test locally by pasting your trophy code into the OS13k's console app
149 |
150 | ## Contribuitors
151 |
152 | Most of the OS was created by myself, but there were many other people helping out. Thank you to everyone for their efforts, I could not have done it alone!
153 |
154 | - Keith Clark - ZzFXM
155 | - Tomxor - Don't Fall
156 | - Niklas Berg - Shedding Snake
157 | - Pavel - Visualizer and 404
158 | - Rodrigo Siqueira - Sticky Note
159 | - Kang Seonghoon - Roadroller
160 |
161 | Additional help by...
162 |
163 | - Katkip, Jaburns, Xem, Rebecca König, Cantelope, DaSpider, Lionleaf, Yurume, Magna, Thomas Brierley, Nicholas Ortenzio, Yuanchuan, Jani Ylikangas, Martinn Kleppe, Erik Sombroek
164 |
165 | 
166 |
--------------------------------------------------------------------------------
/apps/camera.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |