Writing programs using JavaScript
To write programs using JavaScript instead of VPython, change the first line of the program to "JavaScript X.Y", where "X.Y" is the current version. Equivalent headings are "GlowScript X.Y" and "GlowScript X.Y JavaScript".
The documentation describes the syntax to use when writing VPython code. Here is the relationship between the VPython documentation and what you would write in JavaScript:
b = box(pos=vec(2,1,0, color=color.cyan) # VPython
b.axis = vec(1,1,0)
let b = box({pos:vec(2,1,0), color:color.cyan}) // JavaScript
b.axis = vec(1,1,0)
At the moment, the ACE editor used at glowscript.org is an old version that doesn't recognize some (now valid) elements of JavaScript, including in particular async, await, and clas, and as a result statements containing these elements are marked as being in error. Evidently glowscript.org needs to upgrade its use of ACE.
Using async and await
An animation loop must contain a rate(), sleep(), scene.pause(), or scene.waitfor() statement; otherwise the browser will lock up, and it is difficult to kill the browser. Moreover, these statements must be preceded by await, which is inserted automatically when VPython programs are transpiled to JavaScript but must be entered explicitly into a JavaScript program. Here is what these JavaScript statements must look like, including the five other VPython functions that take time to complete and therefore also need await:
await rate(30)
await sleep(2)
await scene.pause()
await scene.waitfor('click')
await scene.capture(filename)
await input()
await winput()
await get_library(webaddress)
await read_local_file(scene.title.anchor)
If you write your own function or class method that includes any of these waiting statements, you must prepend async to the function or method declaration, and all calls to these functions or methods must be prepended with await, as with the functions shown above and in the runnable html file shown at the end of this article.
Also, just before calling input() or winput(), it's a good idea to insert
"sleep(0.1)" in order to make sure that the display is up to date before waiting for input.
At glowscript.org your program is wrapped in an async function, which makes it possible to use await outside any of your functions. If you do not use glowscript.org to prepare your JavaScript program, you may need to wrap your program in an async function, as shown in the runnable html file below. It is not possible to use await outside an async function.
Operator overloading for vectors
Something that is automatic at glowscript.org for JavaScript programs as well as for VPython programs is the "operator overloading" that permits vector operations such as "let v = v1+v2", where v1 and v2 are vectors. If you do not use glowscript.org to prepare your JavaScript program, you will need to write this statement in the form "v1.add(v2)". Similarly, "v1-v2" would be written "v1.sub(v2)", 5*v would be written "v.multiply(5)", and v/5 would be written "v.divide(5)". An alternative is to pass your program through glowscript.org and click "Share or export this program" to obtain an operator-overloaded version of your program.
New in version 3.0: linkage of axis and size; default sphere radius now 1
Starting with version 3.0, JavaScript programs, like VPython programs, link changes in an object's axis to its size, and changes in its size to its axis. Prior to version 3.0, JavaScript programs did not perform such linkage, and the arrow object had a special attribute "axis_and_length" instead of "axis". Using axis_and_length now will cause an error.
A related matter is that, for historical reasons, the default radius of a VPython sphere is 1, which is now also the default
radius of a JavaScript sphere. Formerly a JavaScript sphere did not have a radius attribute, and its default size was vec(1,1,1), so the radius was 0.5.
JavaScript class "constructor" method
When you create a JavaScript class instance the arguments are sent to a method named "constructor". Neither the class itself nor its constructor method can be of type async, which means that in the constructor method you cannot use await. If you do need to use await in some part of the initialization of a class instance, write the program like this:
class C {
constructor(n) {
C.n = n // a class variable
this.x = 20 // a class instance variable
}
async init(y) {
this.y = y
print("Sleep...")
await sleep(2)
print(C.n, this.x, this.y) // displays 10 20 30
}
}
let a = new C(10)
await a.init(30)
Passing methods as arguments to other functions
Suppose you create a class named C with an instance named c1 and a method c1.m. You might wish to create a reference in the form let cm = c1.m (no parentheses) and pass it to a function f in the form f(cm). In that case, you need to write the assignment to cm like this: let cm = c1.m.bind(c1). Again, this is done automatically for VPython programs.
A runnable html file
If you store the following program in an .html file and then double-click the file to invoke a browser, you should see a rotating cube (adjust the version number for the glow library appropriately):
<div id="glowscript" class="glowscript">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link type="text/css" href="https://www.glowscript.org/css/redmond/2.1/jquery-ui.custom.css" rel="stylesheet" />
<link type="text/css" href="https://www.glowscript.org/css/ide.css" rel="stylesheet" />
<script type="text/javascript" src="https://www.glowscript.org/lib/jquery/2.1/jquery.min.js"></script>
<script type="text/javascript" src="https://www.glowscript.org/lib/jquery/2.1/jquery-ui.custom.min.js"></script>
<script type="text/javascript" src="https://www.glowscript.org/package/glow.3.2.min.js"></script>
<script type="text/javascript">
window.__context = { glowscript_container: $("#glowscript").removeAttr("id") }
async function __main__() { // async wrapper permits use of await outside your own functions
var vector = vec // optional: makes vector a synonym of the fundamental vec
let scene = canvas()
let b = box({color:color.cyan})
async function f(obj) { // needs async because f() contains an await
let t = clock()
while (true) {
await rate(100)
obj.rotate({angle:0.01, axis:vec(0,1,0)})
if (clock()-t > 3) break
}
return 25
}
let x = await f(b) // needs await (inside async __main__) because f() contains an await
print(x)
} // end of __main__ wrapper
__main__()
</script>
</div>