Writing Programs in 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”.
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 class, 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: 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 ofaxis
. Usingaxis_and_length
now will cause an error.Default sphere radius now 1: 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¶
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¶
Web VPython programs may be exported to html. The following runnable html file is produced from Javascript. Adjust the version number for the glow library appropriately, then save the following as an .html file and open it in a browser (double-click):
<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>