hi everybody when it when I give a
presentation somewhere i'm pretty much
contractually obliged to have some lists
cartoons in there somewhere
so I was trying to think of what kind of
list cartoon i could start with and then
it kind of occurred to me why I wanted
to bring in the you know the whole
politics thing that's happening right
now and then I realized all the closure
logo could be kind of like a basketball
and so so then I had this idea and I
thought what we need some tension in the
cartoon so Obama has to trip on
something and at first I was thinking
maybe I should make him trip maybe maybe
Mitt Romney is throwing some money any
trips on it really throwing money on the
park but then like that has nothing to
do it lists so i had to come up with
something to trip on that has to do it
lists and the solution to this of course
was slime
alright i'm going to talk about web GUI
this is a functional client-side
JavaScript framework for closure script
now there's a million client-side
frameworks I'm sure if you google the
term yet another JavaScript framework
you'll find 10 different frameworks with
that name
now I'm so you know the most important
thing when you're looking at a framework
is you know what were the goals of the
designer and well before I didn't get
into that let me just talk about some
basic terms most people here probably
know it but since there will be a video
i figure i should that define a few
things the first one is what is the Dom
so when you write HTML and again and the
web browser displays your webpage it
creates this internal data structure
that represents the webpage and the
thing about that data structure is its
mutable so when you write JavaScript you
can actually muck around with that Dom
to change the web web page in real-time
and so closure people don't really like
things that are mutable ah now the
second thing is what is ed n so edn is
basically the the serializable part of
of closure so it has the maps the
vectors so it's a subset of closure of
this the data structures that are
serializable so if you're not familiar
with en if if you just pretend every
time instead of saying ed
and I say syntax expression or I say
json you'll pretty much know what i'm
talking about the final thing is what is
Ajax so in i'm using a Jackson in the
broadest sense meaning anytime after a
web page loads if the client talks to
the server i'm going to call that Ajax
even though of course we don't use
JavaScript and we use closure script and
we probably wouldn't use XML we would
use edn to send to the server now so
what what are the goals of my framework
ok the first thing is we want to do
things the closure way so i tried to
come up with something that that I feel
embodies the closure way now of course
everybody has a different opinion on
what the closure way is and there is no
one person who gets to decide what the
closure way is well maybe there's
there's one person but anyway the next
thing I really like functional
programming and so I really wanted to do
something that let me write most of my
code as functional now the last goal is
something very specific what I would
like is that that we just have an atom
that contains edn data so you're
serializable closure data structures and
that I want to be the dom so I want to
just be able to stuff information into
this adam and i want the framework can
just synchronize that with the Dom for
me now on now the standard way that we
represent web pages in the closure
community is to use hiccup and and
that's what i really want the EDM to
look like so now there's other people of
course who have done this sort of thing
so one example is the the single
framework that Kevin lionize come up
with and there's there's other folks
that have played around with these ideas
and also in other languages like Haskell
you know this this kind of idea is isn't
that uncommon but what i've done is i've
tried to take this idea to the extreme
to see what it would look like and and I
I feel like you end up with something
that's actually a pretty Pleasant
framework to do web development
ok
so there's four parts the web for these
are pretty much independent libraries
and i'm going to go through them all one
after another the first one and the most
low level is web GUI Dom now Webb
freedom has only one job and that is to
take a closure Adam and synchronize it
with with the Dom of the browser now I'm
let me so let's look at the demo
application that does this so so here's
the simplest application possible
it has two text fields and it adds them
together and then shows the result now
let's look at the code for for that
example so here's here's the server-side
HTML this is the only time i'm going to
show anything server-side basically you
can see this is brain-dead HTML all it
does is loaded stylesheet load
javascript file which is of course
closure script and then everything else
from their happens in the JavaScript so
are all examples have exactly the same
structure HTML
Oh
alright i was warned to use the ethernet
and now it's coming to buy me here let
me see if we can switch over
that error
ok
yes
okay that looks like it's performing
well I should be up and running again in
just a moment here
where was I ok so let's look at the
actual client side code that runs this
example now the thing about this code is
it's it's very ugly because all this
low-level library does is it only solves
the last of my three goals which is
synchronizing with the Dom but it
doesn't solve the first two it's not
functional and it's not the closure way
so I'm so what you can see here what we
do is you know the library's then we
here's my dom is just an atom that
starts off as nil and then there's a
function where I swear that I calling
the library saying I want this to be my
dom called F tom then we have a couple
of global variables here that we just
hit 20 which are the a and B values are
going together and then we have this
function called render all and this is
actually just generates hiccup and it
embodies our webpage so this is the
world where the HTML is generated and
you can see all we do it down at the
bottom is we just add a and B to show
our result now we need a fuck function
that actually takes this rendering and
sticks it into the Dom that's right here
so this is the the imperative function
here and then we want to actually call
that so that when you first load the
page we see this instead of a blank
webpage and then here's the rest of the
code
here's a validator because of course job
browsers don't have text fields that
numerically are limited numerically so
you have to implement that yourself so
we have to make sure they type in a
valid integer and then here's here we
have watch functions and this is what
actually performs the work so you can
see if i go back right over here I i did
declare a watch inside my HTML so this
is an extension beyond pick up that you
can attach watches two elements in your
data structure and then so so why am I
using watches why am i not using events
so so personally I've I found working
with this that events are really not
very good for for doing functional
programming like this because events
only have a lifespan that's
instantaneous so an event happens then
it's over
what really when you do
functional programming what you want is
you want to deal with entities that have
a lifespan so so the so what I would
really like is instead of getting an
event saying old somebody change text
but i really want to see is just you
know what what the element look like
before they change the text and what did
the element look like after they change
the text and if I have access to those
two things that I don't need some
complicated event object and I no longer
have to read the JavaScript
documentation on the event object to
remember when I deal with a text area
filled what-what attribute do I have to
check on the event object to figure out
what the new value is or whatever like
that's that's just a lot of complexity
and of course we're familiar with this
enclosure in the closure world because
we don't have you can attach watches to
two atoms enclosure and there's no such
thing as add event to an object to a
Adam so we could somebody could have
written that right where every time you
modify an atom you also send in an extra
data structure that says here's what i
did and why I did it and then you could
read that event object but but that's
that's just extra complexity we just
want to see the before and after and we
can handle everything ourselves so and
and the way you can describe this is
basically show me don't tell me I don't
want you to tell me what the event that
happened I just want you to show me the
the before-and-after of the event so so
that's why we have watches so you can
see just to run through this you can see
that when you add a watch to your H your
Dom you are you get you get as a result
the old element and the new element and
then this new element we can actually
just read the value right out of it it's
it's a you know it the by the way hiccup
of course has a lot of shortcuts where
you can you can if you don't want
attributes on an object you can just
leave off the attributes or you can do
things like where you add a class
identifier or ID directly to the tag
name so on the web free library will
automatically normalize that so that you
your guarantee that the first item is
all in the list after the tag name is
always an attribute list
so uh so then we just need to check is
this a valid integer and if so then
we're going to set our global variable
equal to that new integer and then of
course we have to call up the Dom so
that we see the result and then the
second one is exactly the same thing as
i said this this is on purpose ugly code
it's just supposed to show the simplest
thing possible here so here's the
resulting program so i can go in here
and it's going on okay
it's really odd
alright let me oh you know what i think
i've loaded an old version of this
there we go so okay great so i can type
in i can just type in numbers here and
you can see it automatically updates the
result now when this page first loaded
it basically initialize the page with
the HTML from the render all function
but then after the initial load it it
won't regenerate all the HTML instead
what it does is it performs a delta
calculation between the old ate the
previous HTML in the new HTML and it
only updates the the minimum amount
possible to bring it into the new state
that's why when i type things we
actually see that the cursor here the
first item remains focused because if i
was to just delete all of this and
rebuild the entire page it wouldn't have
focus anymore so so there's and there's
many other reasons why you never wanted
to a wholesale replacement of the page
for instance CSS transitions have have
dependencies that will cause your
animations to look wrong if you if
you've got a race items and replace them
with identical items and this will
automatically now validate wrong input
so if i try to type in alphabetic
characters as I'm doing now nothing
happens because as you saw only when it
validated did I update my global
variables and those global variables are
decide what appears so
and then also I validate that the number
isn't too long so if i keep in typing
numbers it will limit it if you go much
past that it will start showing it in
scientific notation and it looks ugly
so yeah so that's basically it and for
for the basic example so but as we saw
the code was really ugly for this
so how can we make it better so how do
we make web free pleasant to use well
what we want to do is we want to add
another item into this chain we want to
create a we want to make this functional
and we want to have a world state object
inside the our program and when we when
the when the DA madam changes and our
Watchers get called we just want to
update that that
state object and then that the render
all function instead of taking the
previous dot version of the the HTML it
actually takes the state object as a
parameter and so if we do it this way we
can now I'm right all of our quotas
functional so the Watchers and the
render all functions are now functional
they don't have to talk to the outside
world and and this kind of design is
basically what people used to call
dataflow programming where we have these
these boxes with data structures and
when one of them changes we we propagate
that so so here's the the add-on watch
function i showed you a minute ago and
let's see well how we can improve this
now so first of all we get rid of the
old element and we replace it with a
state so now you can see it receives the
state as a parameter and now when I now
I what I have to do is return the new
state so what I do is I take the state
variable and I i associate to the to the
key a the new value of a in this case
and then and and if that if a was a
valid really just return the state now
this is ugly for several reasons still
because for one reason i'm a believer in
declarative programming and that is
soaked in function isn't very
declarative an HR what I want is I want
to a function where mice my uh the the
body of the function looks like the data
that it operates on the soaked in kind
of breaks that rule what I really wanted
something like this where we load in our
state and then we build a new state and
you can see a state little map literal
in there
we're constructing it and you can see
what the end result of the function
looks like now this has a new problem
though because now you can see there's
references to be the other variable but
we're writing a function to just handle
a right now so I'm we're not we don't
have proper separation of concerns so we
really want to do is we want to say oh I
just want to modify a in the state i
don't want to modify the whole thing so
a so how can we do that cleanly and
still have it be declarative so that's
what the second library for is for it's
called state patches and basically what
this library does is it takes unless you
just a diff description of how you want
the state to change and then it will
apply it to the state for you now what
are so the reason this works is that
when you went yours the state in your
program is always going to be very
similar to the problem domain that
you're dealing with and so when when the
user performs an action that will
usually only modify one or maybe a
couple of variables in your state
because of how you have organized it so
it's highly localized so we want to make
pinpoint changes to the state now this
is different from the the Dom right so
if you have a a website for selling
widgets and the user picks a new widget
the entire webpage may change because
you're showing all the data on this new
widget
however the state on the webpage is
probably only going to change like one
variable that's called select it's a
widget ID or something like that so I'm
so we don't want this for the for
modifying the Dom we just wanted for
modifying the state and usually a diff
has three parts right you things that
have been added things that have been
edited things that have been deleted
well I don't want to worry about all
this stuff because uh usually with a
diff we have to worry about all these
things but you when you create state
enclosure you're usually going to have a
map and so with a map
editing and adding are exactly the same
right if you just reuse a key already
had then it edit it otherwise it adds it
and we can simulate deleting by simply
setting a key to nil and that's almost
as good as having deleted so it's not a
true uh diff with with the system
because you yet but you can't delete
keys but what this allows us to do is we
don't have to do all this bookkeeping of
the three different types of diffe so
here's what the function looks like it's
essentially in its basic form this this
state
dispatch function is just a merge
recursive map merging function so if it
encounters anything that's not a map it
simply does a wholesale replacement and
the one exception is that if you have a
vector in the second the last example
here and you try and the diff that
you're applying into that vector is a
map
then it will treat treat the first item
as an index into that vector and it
turns out that that just from practical
use this is something you often want to
do you often have a vector of similar
items and you just want to modify one
single item in that vector so that's the
one case where you can do a diff on
something that's that's not a map and
and so so I find this particular will
function very useful when you have a
World state object in your program and
that's what I use for this library so
with this new function we can now
rewrite our code like this where we just
send back the value of a and we we don't
have to reference be so now we have
proper separation of concerns it still
looks declarative because we're just
declaring the new new structure of the
state now the last thing i do is if you
saw before i actually have a function
that I would place in here so so I i
rewrote it as a macro so that it looks
more like the atom watch function is
like a command kind of like a function
definition and we don't have to
explicitly send in the function so this
is what after all the transformations
what we end up with and this is a much
cleaner way of writing a watch so what
you see here is this is the second
version of the program and this verb and
this is all the code so you can see here
up here we did we import our stuff
here's our render all which hasn't
changed except that we now use states
state variable that's passed in so it's
functional this didn't change and then
just to make it even more elegant I i
got rid of the code duplication by just
pulling the ID of the element out of the
the the watch result i get back and so I
can just saw a handle a and B
simultaneously so this this is in my
view a pretty elegant way of writing
something like this
now sometimes things don't change the
Dom but we still want to capture them so
as you saw this watcher does it watches
a part of the Dom so what it what if the
Dom doesn't change how do we handle that
well there's a mechanism for putting in
plugins so there's a plugin protocol and
there's basically two different things
that a plug-in dozen web GUI the first
thing is it makes hidden Dom changes
visible so there's there's a lot more
you can do with dom then you can do with
HTML so that didn't format
well that's good so up so in this
example you can see you know we have the
two plungers and you can see the Dom
plunger has all these extra features so
what might what am I talking about here
so I'm let's say you wanted to display
webpage and you want a you have a
scrollbar in your web page and you want
that scroll bar scroll all the way down
to the bottom
well there's no way you can write HTML
that says all place the scroll bar all
the way down on the bottom when you
display this page
the only way to do that is through
JavaScript you have to call the the the
scroll top property on that item
another thing that you can't do is that
obviously a browser knows when you press
the button because when you hold down
the button the image of the button
change it into a press down button but
we don't really have access to that
there's no way to display webpage with a
button already pushed down but there is
a pseudo class in CSS called active now
why that isn't just a regular property
on an item i don't really see any good
reason for that
so with with the this type of model i'm
showing since we're trying to avoid
working with the Dom we have to lift
items that are only available in the Dom
into the regular HTML so that you can
modify them directly and so that's one
thing that you can do with plugins the
other thing is is that you might want
different type of watches so I'm i just
showed you the basic type of watch but
if you're dealing with something like
Mouse somebody's is wiggling the mouse
around the screen and generating all
kinds of points with a mouse move that
doesn't that isn't even a hidden Dom
property so we have to somehow be able
to capture that so the first plug-in i'm
going to show is the scrolling plugin so
the demo of that is right here it's it's
pretty big
sick we just have a scrollbar here and
if i move the scrollbar you can see it
changes the number down here and in the
same way i can go down here and edit the
number 0 another little technical glitch
i should be able to edit the number in
here and it would move the scrollbar so
these two are synchronized so so this is
what the code looks like for this so so
this well this is just the part that
response to moving the scrollbar are
changing the number so the first one
all it does is it just gets the scroll
top of the element which is now lifted
into the regular HTML that gets returned
as the new element variable and we just
set the position in our state to that
new scroll top if they change the the
number in the text field you know we
have a function here called tryparse
that simply make sure it's a valid
number just like we saw earlier and just
set it to 0 if somebody types in alpha
nu alphabetic characters and so it does
the same thing it just sets that same
position variable and that position a
key in the state drives that the values
for both of those items now let's talk
about something more complicated how
would we handle a mouse now the way
people have been handling mice has
actually changed a lot in the last
couple years because people use mobile
devices and so on one thing that you
used to see a lot is that web pages
would give you hints when you have your
mouse hovering over an item or or would
highlight items when a mouse hovers over
to show that they're active but of
course on there is no such thing as
hovering on on a touch device so so
usually most mouse actions now are more
like gestures where we capture when
somebody pushes down on the screen
what movement they do with their finger
and when they release it so that just
capturing that covers ninety percent of
what people want to do and that's what
this plug-in does so if you need to do
hovering which most people don't these
days then you can write your own plugin
that that
add that feature but this simplifies it
by treating gestures as a as a separate
entity so i will let me look show you
the sample code for this so here's a
circle numbers i can click on a number
and you can see it automatically uh
highlights that number out of this the
set of numbers and gives you a little
ghost and I can drag this go somewhere
else and it will add that number
together and we can keep doing that and
we go like this it will keep making
larger and larger boxes so this would be
like you know the start of it of a
simple game like organic what's that
called not gattaca there's a gal con is
sort of a game where you drag planets
together in order to to do strategic
stuff so so this is my idea of how a
modern web app usually handles of input
from the mouse and of course um I want
this to also work on an iPad so here you
can see the same circle on an iPad and I
can click on an item here and I get a
ghost and I can drag out to another
location so this plug-in automatically
handles both of those modalities now
let's look at the actual code all I'm
going to look at is this most of the
code is just involved with rendering
these boxes in in the hiccup format this
is the only code that actually performs
an action and the important thing here
is that this mouse watch which is a new
type of of the watch and it adds an
extra parameter called points which
gives you a the entire vector of all
points as the mouse movement happened
between pressing the button before it
was released so that's the one change
and then the other thing is that it
gives you the first element and last
element of the motion so if somebody
drives from one point to another you get
the the first and last element and so
this is the only of the function that
exists in web 344 mouse interaction and
it handles dragging and it handles you
know mouseup mousedown mousemove just in
this one function and and of course we
don't want to write we don't want
function like mousedown right because
what do you usually do in mouse down you
there's a global variable called uh you
know is mouse down and you set it equal
to true so that later on in your mouse
move you can then have something happen
only when the mouse was down so that's
already imperative programming so having
treating them a mouse action as a
gesture we can be completely functional
and that the most interesting thing that
I'm just going to explain is this sizes
part so this is the part that that that
increases the the the size in in our
state to be the new size where it adds
the the the location we're moving from
to the location we're moving to and the
interesting thing is that that as you
saw I i if i decide not to let go the
mouse on one of those bubbles but
instead just go into the white area it
will cancel my action and in the same
way if i move if I move into a box and I
move my finger away i wanted to subtract
that value that that was temporarily
shown as a preview so so so so i don't
so this code handles all those cases so
it handles the previewing it handles the
the addition and subtraction because the
state that has passed in here is
actually the state at the time the mouse
was pushed down so on so what this means
is one of the nice things about
functional programming is so if we have
a world state we can keep old copies of
that state and for mouse movement the
most important state that we want to
keep is the one at the time the mouse
was pushed down because if the user
wants to cancel the action halfway in
there gesture we want to be able to get
them back to that default state and so
so this one function handles all those
things at once
now let's look at a slightly more
complicated example this is just a
simple calculator so this is this has
all the functionality of a typical
eighties a desk calculator and you know
it does it has memory it it it properly
performs overflows of so if i multiply
this by large number
let's do that again then Allah say
overflow and and and there's like tricky
things you have to do like if somebody
presses the the decimal point and then
types in trailing zeros you have to
display those so it handles all those
cases so this program in web GUI is a 92
lines of code and uh just to show you
quickly here's here's the actual code i
won't go into this in detail you can see
just declare some data structures here
and then here we handle all the cases of
overflow and the decimal point and all
that
here's where we watch the watch
functions for the number keys and the
operator keys and then the important
thing is that right down there when you
use the the framework library that's
what gives you this the the ability to
work with state so that's a layer a
library that is on top of the lower
level bomb library you just call this
function launch app and that one
function out of all those ninety two
lines of code that's the only one that's
truly imperative code everything else is
just regular functional code so now
let's talk about the elephant in the
room which is performance right because
we're every time a user actions
performed we generate this giant hiccup
structure that represents the entire
webpage then the my library performs the
Delta calculation on that data structure
and then we update only that part so all
of that has to take time so just to see
a what kind of effect that has
here's an example where i just created
40 of these calculators so this page has
over 2000 dom elements in it and each
individual calculator you can press the
buttons and it does work now if i do
timing on this on my laptop
it takes about 250 milliseconds to
handle a keypress so that's not that's
not really great i mean we want to to
have a few responses we want at least 20
frames a second response which would be
like 50 milliseconds so I think to make
this approach really work i would want
this particular example 2
in 50 milliseconds now there would be
one easy way to do this immediately and
that would be to simply write the
library itself most of the work inside
of the library in JavaScript and I've
done that in the past created similar
libraries that they do this sort of
thing and i found that that JavaScript
will give you this level of performance
now I'd like to avoid doing that I'd
like to keep the library enclosure code
but even if I with if it was rewritten
partially in JavaScript would of course
be invisible to the user that can just
continue to write closure script now
let's talk about Ajax so that's the next
thing we have to deal with so so Ajax is
is tricky because we have a are this
beautiful functional program and anytime
we talked to the server
it means that we have side effects so
how do we deal with that now one way of
doing it is that we have a request queue
in our state Adam so what we can do is
well in all our functional code we could
if the functional code realizes all we
need something from the server we we we
have inside the state variable some kind
of Q where we say Oh fetch this extra
thing from the server for me and then we
have some other imperative code that pop
stuff off of that Q you don't want to do
that
just like i was talking before with
events you really don't want to deal
with things that only have an
instantaneous lifespan and and putting
these requests into a q-and-a state Adam
that's not really the job of the state
the job of this the state Adam is 22 to
show things that persist over time not
to just for its instantaneous second
popping a little message and then have
somebody else consume it
that's not what state is for what I find
just in terms of my own practices what
works for me is to always think of Ajax
as a flaw in the state object and so the
way you want to think about this is at a
inside our closure script program we
have this this world state object that's
supposed to hold all the state for our
program now the moment we do an AJAX
call we we can see that this is a lie
because the only reason we would do an
AJAX call is because there has to be
some other state out in the
world that we either want to write to or
read from so the moment we do that we we
know that you know this this world state
that we have is is a corrupt system and
so the weight of the best way to that I
like to think of it is that the places
in our program where we where we have to
communicate with ajax it's almost like a
form of corruption in our world state
for our program and and and as we know
with corruption corruption tends to
spread like a virus so if you have
corruption somewhere in your data and
you keep using that data it's gonna
break other things in your program so I
like to think of server communication as
a virus so the idea is that we want most
of our functional code that's to say to
itself
oh this world state that's all there is
in the world then but then there's
another part of your program that knows
no no this isn't true there's other
state now what will happen is that if
you think that your world state is the
entire world then eventually you're
gonna you're gonna run into problems and
you're you're gonna run into the
situation where you get corruption in
your program because there's updates
happening from a separate location so
now if you think of it that way if you
think of it as a virus there's three
different ways out of that this that you
can handle ajax inside of your program
so let me give you give you an example
let's say you have a web page for
browsing widgets somebody clicks on a
picture of a widget and now we want to
load in all the information about the
widget well that information about the
widget is on the server and we're
pulling with an AJAX call so now all of
a sudden the functional our state has
like an item called selected widget ID
but but their butt inside of the state
there's the information about that which
it doesn't exist so so now our state has
become corrupt
well we might get lucky because it might
be that uh the the so we just just
display a blank page because we don't
have anything better to show but then
immediately a couple seconds later the
user clicks on another item and we do
have the information about that the
other thing is we can try to contain it
so we can say okay if you click on an
item
and we can't display that information
let's stop you from doing anything else
until we get a message back from the
server with with the result and the
final thing is we can cure it
so the only way to cure this virus is to
get a response back from the server so
what we do so so so I created a
calculator with persistent memory and
the only way the program differs from
the previous program is that i set the
memory instead of setting it to 0
I said it to unknown so this is like a
little bit of corruption in our program
and so if we try to read that memory
that means the amount displayed on the
calculator will also be unknown which is
which is invalid number and so we this
purpose
corruption is propagated but however i
put a watch on the state variable and
that watch checks to see if the amount
in the calculator is unknown and if so
it's it sends off a ajax request to get
the memory that stored on the server for
this calculator at and simultaneously if
um if somebody changes the state and the
amount was already unknown in this
particular example i simply prevent
people from doing anything with the
calculator while the amount is an
invalid value for containing the damage
caused until we get a proper messages
response you can make this more fancy
and let the person continue working as
you know if they want to do things like
the AC button then we don't care that
there's a corrupt memory because they'll
get over it anyway so you could get
really fancy and let people work a
little longer even if the Ajax resulted
in packet so this is just a a model that
i found useful of working with this type
of library and doing Ajax and and this
is completely orthogonal to web food so
there's nothing in web fluid that deals
with ajax but you can just have a
separate function that you used to watch
your state and and and have that
function be imperative and it's
responsible for talking to the server
and the rest of your code can be
completely unaware that anything like
Ajax exists or that it has to talk to
the outside world so i won't bother
demoing that
out so the very last thing i want to
show is in something that's a little
flash here so this is an example of a
little inverse kinematics and jen i
wrote on top of web fully so here we
have a little guy and I can click on his
arm here and the arm will move like that
and this is completely functional
wonder-percent functional code i can
move him every which way here and I can
move his little ball around here and so
yeah so if you download the example on
the the web three examples on github so
it's under dr. code web GUI you can play
around with this this inverse kinematics
library and yeah and you can see the
performance is pretty good and this will
work on an iPad so it's a little little
bit slower on an iPad and that's pretty
much my demonstration so all right I
think that's it right
thanks everybody