Chris Oliver's Weblog
- All
- F3
- JavaFX
- Programming
- Research
JavaOne 2008
We presented our JavaFX keynote demos yesterday, including Moontank
A true 3D, multiplayer game written in JavaFX script, which includes spatialized audio via OpenAL. Moontank was designed by Anthony Rogers of Tomato design fame. The 3D models, textures, and audio assets were created by Anthony. The JavaFX application itself was written by Gerard Ziemski, and the OpenAL support thanks to Ken Russell. The actual moon images (provided by NASA) are also thanks to Ken.
Those at JavaOne can come by the JavaFX Pod and play it yourself.
Posted at 09:54AM May 07, 2008 by Christopher Oliver in JavaFX | Comments[3]
Key-Frame Animation
It's unfortunate that OpenJFX currently isn't a real open-source project. As such, it gives the appearance that progress isn't being made with JavaFX Script. Nevertheless, evolution has occurred, albeit internally.
I've worked on replacing the animation framework in JavaFX with something more complete and consistent. This was always intended to happen. The presently available animation mechanism (the "dur" operator) was a temporary solution added to F3 more than a year ago, was never thought to be an adequate solution, and clearly isn't to anyone who has ever actually tried to use it.
The supported animation idiom is now that of "Key Frame" animation, as in traditional animation and visual design tools. My view (after significant research) is that this same idiom is also the most effective technique for describing animations programmatically. The concept is that you describe the animated state transitions of your "scene" by declaring start and end "snapshots" of state at certain points in time, and declare interpolation functions to calculate the in-between states.
Given this input the system can automatically perform the animation, stop it, pause it, resume it, reverse it, or repeat it when requested. Thus it is a "declarative" animation technique.
Programmatically such "snapshots" consist of specifying the values for JavaFX variables - local variables and instance variables.
Here are some examples:
- Simple A very simple example, which demonstrates simultaneous animation of the rotation, scale, and fill color of a rectangle. Ball Consists of three concurrent repeating animations: one for the x coordinate of the ball, one which animates the y coordinate, and the one which animates the scale to create a "bounce" effect. See line 65 for the animations.
- Motomaxxve Consists of only one keyframe animation whose "position" is bound to a Slider, such that you can manually "play" it by moving the slider. Left-click on the content will play it forward, Right-click will reverse it. One interesting point is that the text animations are dynamically generated.
- EG Consists of several animations (fade, slide): one of which is a kind of ticker-tape view of the presenters at the 2007 "Entertainment Gathering" at the Getty Museum. The others support a slide show view.
- Poker Video poker game. Consists of two animations: 1 for the deal, and 1 for the draw, each of which animates the bet, turning over the cards, and scoring (if you have a winning hand).
You'll want to take a look at the examples for reference as you read the below, I think.
Time Literals
Since time is fundamental to animation, I've added time "literals" to the language. These are instances of the following primitive class:
public class Time extends java.lang.Comparable {
public attribute millis: Integer;
public attribute seconds: Number;
public attribute minutes: Number;
public attribute hours: Number;
public operation toMillis():Integer;
public operation add(other:Time):Time;
public operation subtract(other:Time):Time;
public operation multiplyBy(n:Number):Time;
public operation divideBy(n:Number):Time;
public operation equals(other): Boolean;
}
Numeric Literals with one of the suffixes (h, m, s, ms) are interpreted as Time literals, e.g:
2s == Time {seconds: 2}; // true
Relational operators and arithmetic operators other than % are overloaded for Time types:
2.5s < 5000ms; // true
2.5s * 3 == 7.5s; // true
Timelines
A KeyFrame describes a set of end states of various properties of objects at a certain time instant relative to the start of the animation, together with interpolation specifications to calculate the in-between values relative to the previous frame. Lack of an interpolation specification indicates a "discrete" animation, meaning the value of the property instantaneously transitions to the value given in the key frame at the time instant of the key frame. A Timeline is an object that contains a list of key frames, together with operations to control the overall animation, to start it, stop it. repeat it, etc.
Timelines are instances of the following class:
public class Timeline {
public attribute keyFrames: KeyFrame*;
public attribute repeatCount: Number;
public attribute autoReverse: Boolean;
public operation start();
public operation stop();
public operation pause();
public operation resume();
public operation reverseNow();
public attribute position: Number?; // unit time interval
}
where KeyFrame is:
public class KeyFrame {
public attribute time: Time;
public attribute keyValues: InterpolatedLValue*;
public attribute action: function()?;
public attribute timelines: TimeLine*; // included timelines
}
InterpolatedLValue has the following definition:
public class InterpolatedValue {
public attribute value: Object;
public attribute interpolate: function(values:Object*, unitTimeInterval:Number):Object?;
}
public class InterpolatedLValue extends InterpolatedValue {
public attribute target: &Object;
}
InterpolatedValue describes a pair (<value>, <interpolation_function>).
InterpolatedLValue describes a triple (<target property>, <value>, <interpolation function>).
Properties
Specification of the target property required adding "pointers" to the JavaFX script language. Pointer types are declared with the "&" symbol (as in the target attribute above), and pointer instances are obtained with the unary "&" operator and dereferenced with the unary "*" operator, as in C, e.g:
var x = 2;
var px = &x;
*px == 2; // true
*px = 3;
x == 3; // true
Properties in JavaFX are pointers to local variables or object attributes. In cases where these are "sequences" a pointer to an element of such a sequence is also a valid property.
Syntax
Although KeyFrame animations are normal JavaFX objects, special syntax is provided to make it easier to express than is possible with the standard object-literal syntax.
The "tween" operator is a a literal constructor for InterpolatedValue.
100 tween LINEAR;
is equivalent to
InterpolatedValue { value: 100, interpolate: LINEAR:Number }
The "=>" operator provides a literal constructor for a list of InterpolatedLValues:
var x = 2;
x => 100 tween LINEAR;
is equivalent to the following
var x = 2;
InterpolatedLValue {target: &x, value: 100, interpolate: LINEAR:Number};
However, you can also apply "=>" to a whole set of object properties using an object-literal like notation rather than just single property or variable, for example like this:
var rect = Rect {};
rect => {height: 400 tween EASEBOTH, width: 500, fill: blue tween LINEAR, clip: { shape: Rect => {height: 500, width: 600} };
The second line above is equivalent to this:
[InterpolatedLValue {
target: &rect.height
value: 500
interpolate: EASEBOTH:Number
},
InterpolatedLValue {
target: &rect.width
value: 500
interpolate: null
},
InterpolatedLValue {
target &rect.fill
value: blue:Color
interpolate: LINEAR:Color
},
InterpolatedLValue {
target: &((Rect)rect.clip.shape).height
value: 500
interpolate: null
},
InterpolatedLValue {
target: &((Rect)rect.clip.shape).width
value: 600
interpolate: null
}]
Finally, the "at" and "after" operators are literal constructors of KeyFrame objects:
var x = 2;
var rect = Rect {...};
at (2s) {
x => 2 tween LINEAR;
rect => {width: 400 tween EASEBOTH, fill: red tween EASEBOTH};
trigger {
println("at 2 seconds...");
}
}
after (5s) {
x => 100 tween EASEBOTH;
}
The "trigger" clause allows you to associate an arbitrary callback with the key frame.
The time specified by "at" is relative to the start of the Timeline. The time specified by "after" is relative to the previous key frame.
The first example above is equivalent to:
KeyFrame {
time: 2s
action: operation() { println("at 2 seconds..."); }
keyValues:
[InterpolatedLValue {
target &x
value: 2
interpolate: LINEAR:Number
},
InterpolatedLValue {
target: &rect.width
value: 400
interpolate: EASEBOTH:Number
},
InterpolatedLValue {
target: &rect.fill
value: red:Color
interpolate: EASEBOTH:Color
}]
}
Timelines and KeyFrames may be composed hierarchically - a KeyFrame may use the "include" operator to include any number of Timelines, in which case the key frames that make up the included timelines are merged into the containing timeline at the time instant of the key frame. The above "Ball" example demonstrates this, reproduced here:
var ax = Timeline {
// x
keyFrames:
[at (0s) {
x => 0;
},
at (10s) {
x => 700 tween LINEAR;
}]
autoReverse: true
repeatCount: INFINITY
}
var ay = Timeline {
// y
repeatCount: INFINITY
keyFrames:
[at (0s) {
y => 0;
},
at (2.2s) {
y => 375 tween SPLINE(0, 0, .5, 0);
},
at (2.25s) {
y => 375;
},
at (4.5s) {
y => 0 tween SPLINE(0, 0, 0, 0.5);
}]
}
var sxy = Timeline {
// scale x y
repeatCount: INFINITY
keyFrames:
[at (2.15s) {
sx => 1;
sy => 1;
},
at (2.25s) {
sx => 1.2 tween LINEAR;
sy => .7 tween LINEAR;
},
at (2.5s) {
sy => 1 tween LINEAR;
sx => 1 tween LINEAR;
},
at (4.5s) {
sx => 1;
sy => 1;
}]
}
var clip = Timeline {
repeatCount: INFINITY
keyFrames:
at (0s) {
include ax, ay, sxy;
}
}
In the above example, the "clip" timeline combines the other three animations. Playing "clip" will play all the animations simultaneously (yet still taking into account each ones individual repeat behavior).
Posted at 10:22AM Dec 20, 2007 by Christopher Oliver in JavaFX | Comments[105]
Compiled JavaFX Script
The JavaFX script compiler is still a work in progress, but here's an very basic implementation of the Bubblemark application using it.
We're still working on implementing the full JavaFX script runtime with the compiler. The above version is just using a provisional runtime and is just animating an image and doesn't implement the "bubbles" with vector graphics.
However, even so, the footprint and performance is now on par with what you should expect from the eventual production quality JavaFX script implementation.
Note: on some systems OS-level timer-resolution will limit your fps. E.g. you'll see a max of 64 fps for any of the bubblemark examples.
Posted at 06:19PM Nov 15, 2007 by Christopher Oliver in JavaFX | Comments[18]
Bubblemark
This posting says some nice things about JavaFX, but also points out that in its current incarnation its performance is lacking:
The first thing you’ll notice is that it’s very slow. The demo takes a very long time to load and start, and real time performance is disappointing. Here is how it compares:
JavaFX — 14 fps
Firefox + Silverlight (JavaScript) — 56 fps
Firefox + Flex — 62 fps
Adobe AIR — 62 fps
Firefox + Silverlight (CLR) — 99 202 fps (update: 202 fps after fixing main timer’s latency)(All tests were performed on Pentium M 1.7 machine under Windows XP SP2)
It is 4.4x slower than Flash and 7x 14x slower than Silverlight (CLR-based variant).
On a positive side, development with JavaFX is fun, the language is very compact and efficient for building complex dynamic layouts. There is a great converter to JavaFX from SVG format and JavaFXPad is done nicely too.
If only it were 5x faster!
Of course, our intention is to provide hardware-accelerated vector graphics and compile JavaFX script to JVM bytecode getting the full benefits of the hotspot virtual machine, and to provide an improved deployment system, and we're working on that.
The Bubblemark performance described above is caused by
- Download time - size of the deployment unit - The bubblemark JNLP doesn't use pack200 compression so the download of the JavaFX runtime + the app is ~2.1 MB
- Performance of the JavaFX interpreter in doing the collision detection - which is currently probably 50-100 times slower than doing it in Java
- Lack of caching or hardware acceleration of the vector graphics and gradients that make up the ball
- Use pack200 compression for the Jar files
- Do the collision detection in Java code
- Cache the vector graphic at the application level - since once created it's static in this application
Below's a version of Bubblemark in JavaFX that does that.
Here's a link to the source code
Posted at 02:30PM Aug 25, 2007 by Christopher Oliver in JavaFX | Comments[43]
Programming Animations in FX
![]() |
![]() |
My goal is to make it possible for the programmer to specify the required behavior and still be able to actually understand what his code is doing.
Animations are fundamentally updates. They assign values to properties (or invoke processes that do such) at certain time instants.
The traditional animation loop
while (animationIsRunning) {
// update stuff
wait(interval);
}
is easy to understand, but it has several drawbacks:
- It requires a dedicated thread - introducing data synchronization issues
- It doesn't handle jitter - "update stuff" can take variable amounts of time at each iteration, making the animation look choppy.
- Since the time consumed by "update stuff" is included in the loop, the animation will exhibit different behavior based on the speed of the processor.
FX provides a time iterated for loop which (I think) is as easy to understand but overcomes these limitations:
println("before the animation");
for (i in [1..10] dur 10000) {
// update stuff
}
println("after the animation");
The above loop describes a discrete animation which executes every second for 10 seconds. In addition, the loop body is always executed in the current thread. However the loop still blocks until all 10 iterations are complete. Currently, this is implemented with the Event Queue Hack, but proper support for continuations could be provided via code generation by the FX compiler. Since the loop blocks, it can be intuitively incorporated into a larger sequential process, e.g;
Rect {
fill: green
height: 50
width: 50
var x = 0
var y = 0
x: bind x
y: bind y
onMouseClicked: operation(e) {
for (i in [0..100] dur 1000) {
x = i;
}
for (i in [0..100] dur 1000 {
y = i;
}
}
}
The above moves the rectangle from (0, 0) to (100, 0) over 1 second, and then moves it from (100, 0) to (100, 100) over 1 second, when you click the mouse on the rectangle.
Now, sometimes we want animations to run in parallel. For this case, a nearly identical construct is provided, namely the update trigger:
trigger on (i = [1..10] dur 10000) {
// update stuff
}
println("after the trigger, but the animation is still running");
In this case the body of the trigger is executed 10 times, once per second. However, unlike the for loop, the code after the trigger executes in parallel with the trigger's body. The body of the trigger is still executed in the same thread, however, when the timer triggers it:
Rect {
fill: green
height: 50
width: 50
var x = 0
var y = 0
x: bind x
y: bind y
onMouseClicked: operation(e) {
trigger on (i = [0..100] dur 1000) {
x = i;
}
trigger on (i = [0..100] dur 1000) {
y = i;
}
}
}
In this example, the animations of x and y occur in parallel and the the rectangle moves diagonally from (0, 0) to (100, 100) over 1 second.
In the case of discrete animations such as these the "frame rate" for the loop body is duration/(sizeof input-1). For cases where an animation is conceptually continuous, a better approach is often to use a predefined (high) frame rate and interpolate between the values for each frame using some interpolation function. The syntax is open to discussion but here's how that works right now:
Rect {
fill: green
height: 50
width: 50
var x = 0
var y = 0
x: bind x
y: bind y
onMouseClicked: operation(e) {
trigger on (i = [0, 100] dur 1000 motion LINEAR) {
x = i;
}
trigger on (i = [0, 100] dur 1000 motion LINEAR) {
y = i;
}
}
}
LINEAR is a predefined function that performs linear interpolation (you can plug in your own instead). The interpolation function is of the form
function<T> (inputValues:T*, elapsedTimeAsUnitInterval: Number): T
As a shorthand for animating individual properties a form of the the assignment statement is also provided:
x = [0, 100] dur 1000 motion LINEAR; y = [0, 100] dur 1000 motion LINEAR;
which is equivalent to the two triggers above in onMouseClicked().
Finally, for the case where the for loop actually doesn't do anything:
for (i in [0, 1] dur 1000) {
// don't do anything
}
we have a shorthand called "wait"
wait(1000);
Below are a few test cases for these constructs:
-
Video Poker
Standalone FXPad -
Motorola Intro
Standalone FXPad -
Simple Path Animation
FXPad
Posted at 04:03PM Aug 09, 2007 by Christopher Oliver in JavaFX | Comments[11]
Hotspot vs Adobe Tamarin VM? No contest.
Several people have told me of Adobe boasting about their Tamarin JavaScript VM, so I decided to look into it myself. I ran 100 iterations of the same Takeuchi Benchmark from my previous post with both Tamarin and Java SE 1.6 Hotspot VM and for this case I found Hotspot more than 25 times faster (see below).
Surprisingly (or maybe not) Adobe's ActionScript compiler (which generates the byte-code for Tamarin) itself is actually a Java application.
Here's Tamarin:
function tak(x:Number, y:Number , z:Number) {
return y >= x ? z : tak(tak(x-1, y, z),
tak(y-1, z, x),
tak(z-1, x, y));
}
var i = 0;
while (i < 100) {
tak(24, 16, 8);
i++;
}
$ java -jar asc.jar -import builtin.abc tak.as
$ time ./avmplus tak.abc
real 0m58.587s
user 0m57.900s
sys 0m0.130s
And here's Java:
public class Tak {
public static double tak(double x, double y, double z) {
return y >= x ? z : tak(tak(x-1, y, z),
tak(y-1, z, x),
tak(z-1, x, y));
}
public static void main(String argv[]) {
for (int i = 0; i < 100; i++) {
tak(24, 16, 8);
}
}
}
$ time java -cp . Tak
real 0m2.231s
user 0m2.143s
sys 0m0.061s
Interestingly, I also tried the Mozilla Rhino JavaScript engine (which I contributed to years ago), which is a pure Java implementation of JavaScript with this result:
$ time java -jar js.jar -opt 9 tak.js real 0m31.944s user 0m31.718s sys 0m0.181s
Posted at 08:11PM Jul 26, 2007 by Christopher Oliver in JavaFX | Comments[58]
First steps with the JavaFX Compiler
Thanks to the efforts of Robert Field, Lubo Litchev, and Jonathan Gibbons of the Javac team, as well as Per Bothner and Brian Goetz (and also thanks to the organizational efforts of Bob Brewin, James Gosling, and Tom Ball) we have the beginnings of a JavaFX to JVM-byte-code compiler built on the same infrastructure as Javac.
Of course, the compiler is still incomplete, but it turns out to be far enough along to try a first performance benchmark (Takeuchi function):
import java.lang.System;
public class Tak {
operation tak(x:Number, y: Number, z:Number):Number;
}
operation Tak.tak(x, y, z) {
return if (y >= x)
then z
else tak(tak(x-1, y, z),
tak(y-1, z, x),
tak(z-1, x, y));
}
var tak = new Tak();
System.out.println("tak(24,16,8)={tak.tak(24, 16, 8)}");
$ time java -cp ".;dist/JavaFX.jar" TakMod
tak(24,16,8)=9.0
real 0m1.333s
user 0m0.010s
sys 0m0.020s
Here's the interpreter:
$ time bin/javafx.sh TakMod.fx compile thread: Thread[main,5,main] compile 0.04 tak(24,16,8)=9.0 init: 69.48 real 1m10.422s user 0m0.190s sys 0m0.130sSpeed improvement for this particular example is a pretty awesome 54x.
Posted at 03:25PM Jul 14, 2007 by Christopher Oliver in JavaFX | Comments[16]
Bidirectional binding
In addition to corrections to local variable binding the next update of the JavaFX interpreter will include extended bidirectional binding, including of logical negation, unary minus, arithmetic, and sequence indexing. Here's a JavaFXPad example you can try out:
// logical negation var a = true; var b = bind not a; assert b == false; b = true; assert a == false; // passes // arithmetic var x = 10; var y = bind -x + 100; assert y == 90; y = 40; assert x == 60; // passes // sequence elements var seq = [1, 2, 3]; var elem1 = bind seq[1]; elem1 = 500; assert seq == [1, 500, 3]; // passes delete seq[1]; assert elem1 == 3; // passes insert 0 as first into seq; assert elem1 == 1; // passes var value = bind elem1; value = 999; assert seq == [0, 999, 3]; // passes
Posted at 08:19AM Jul 10, 2007 by Christopher Oliver in JavaFX | Comments[12]
Preview of JavaFX Interpreter
As I mentioned we will be posting an update to OpenJFX shortly.
The interpreter now has proper support for binding local variables and partially enforces cardinality constraints.
Here's the latest JavaFXPad for you to try it out.
JavaFXPad also has some improvements:
- You can have multiple statements and the value of the last statement will be displayed - without having to specify
return. - Non-visual output is displayed - if the result isn't a Widget, Frame, or Node, the value of the result converted to a string will be displayed in a text area. Thus you can use JavaFXPad like a REPL for experimenting with the JavaFX language.
Posted at 05:33PM Jun 26, 2007 by Christopher Oliver in JavaFX | Comments[6]
JavaFX SVG Translator Preview
It'll take a few more days before we post the code to OpenJFX, but in the meantime here's a preview of the latest version of our SVG to FX translator. The translator converts an SVG document into a single JavaFX class. Each definition id in the SVG is converted into a method of the class that returns the JavaFX equivalent of that element. The generated class itself is an instance of the JavaFX class Node, which means it can be used anywhere in a JavaFX Canvas.
Having this translator makes it straightforward to incorporate graphics created in vector drawing tools into JavaFX programs.
The below demo program consists of an SVG browser that uses the translator to convert SVG into JavaFX source code and then compiles and executes the result. In addition, the browser lets you view the original SVG source as well as the translated JavaFX source.
You can drag URL's that point at SVG files from web pages into the SVG Browser's canvas. It's pretty sad to see how little quality vector artwork is available on the public internet. Anyway, below are a few examples for you to try:
Posted at 11:53PM Jun 24, 2007 by Christopher Oliver in JavaFX | Comments[23]
A Real DryerFox
When I ran across this Apollo example it occurred to me that I could pretty easily create a close Java equivalent of Apollo by combining JavaFX with WebRenderer.
WebRenderer provides a Java Swing component that encapsulates the Firefox browser. In addition, it provides an extensive Java API to interact with the browser including the full DOM. However, WebRenderer isn't free software.
Anyway, below's a screenshot of the JFX version. The dryer turns while a page is loading, but it doesn't have the sound effects (at least, not yet).
Unlike the above cited example (Apollo apparently uses WebKit) that really is a fully functioning Firefox inside the dryer, which is still interactive even while it turns.
Posted at 09:55PM May 26, 2007 by Christopher Oliver in JavaFX | Comments[17]


