[Question]
  [
Input will consist of the following characters:
* `^`: Go up one
* `v`: Go down one
* `▲` or `k`: Go up two
* `▼` or `j`: Go down two
For example, the following input:
```
^^▲^v▼▲^^v
```
would produce the following output:
```
        ^
   ^   ^ v
  ▲ v ▲
 ^   ▼
^
```
Escape sequences that move the cursor such as `\e[B` are not allowed. You must produce the output using spaces and newlines. 
Here are a few more test cases. 
```
▲v^v^v^v^v^v^v^v▲
```
```
                ▲
▲ ^ ^ ^ ^ ^ ^ ^ 
 v v v v v v v v
```
```
^^^^^^^▲▲▲▼▼▼vvvvvv
```
```
         ▲
        ▲ ▼
       ▲   ▼
      ^     ▼
     ^       v
    ^         v
   ^           v
  ^             v
 ^               v
^                 v
```
```
v^^vv^^vvv^v^v^^^vvvv^^v^^vv
```
```
  ^   ^         ^
 ^ v ^ v       ^ v       ^
v   v   v ^ ^ ^   v   ^ ^ v
         v v v     v ^ v   v
                    v
```
  
[Answer]
# [Unreadable](https://esolangs.org/wiki/Unreadable), ~~2199~~ ~~2145~~ ~~2134~~ ~~2104~~ ~~2087~~ 2084 bytes
Supports both `k`/`j` as well as `▲`/`▼` syntax.
In good Unreadable tradition, here is the program formatted in proportional font, to obfuscate the distinction between apostrophes and double-quotes:
> 
> '""""""'""""""""'"""'""'""'""'""'"""'"""""'""""""'""'""'""'""'"""'""'""""""'""""""'""""""""'"""'""'""'"""""""'""""""""'"""'""""""""""'""""'""""'""""'""""'""""'""""""'""'""'""'""'""'"""'""'"""'""""""'""'""'""'""'"""'"""""""""'"""""'""""""'""'""'""'""'"""'""""""""'"""""""'""'""'""'""'"""'""""'""""""'""'""'""'""'""'"""'"""""""""'"""""""'""'""'""'""'""'"""'""""""""'"""""""'""'""'""'""'""'"""'""'""'"""'""""""'"""'"""""""""'"""""""'"""'""'"""""""'"""'""""""""'""""""""'""""""""'""""""""'""""""""'"""'"""""""""'""'"""""""'"""'"""""""""'""'""'""'"""""""'"""'"""'""'""'"""'""'"""'"""""""""'"""""""'""'""'""'""'""'"""'"""'""'""'"""'""""""'"""'"""""""""'"""""""'"""'"""""""""'""'""'"""""""'"""'"""""""""'""'""'""'""'"""""""'"""'"""'""'""'"""'""'"""'"""""""""'"""""""'""'""'""'""'""'"""'""'""'"""'"""'"""""'""""""'""'""'""'""'"""'""""""""'"""""""'""'""'""'""'"""'""""'""""'"""""""""'"""""""'""'""'""'"""'""""""'""'""'""'"""'""'"""""""'""'""'""'"""'"""'""""""'""'""'"""'""'"""""""'""'""'"""'""""""'""'"""'""""""""'"""""""'""'"""'"""""'""""""'"""'""""""""'"""""""'"""'""""'""""'""""""'""'""'""'"""'""""""""'"""""""'""'""'""'"""'"""""""""'"""""""'""'""'"""'""""""'""'""'"""'""""""""'"""""""'""'""'"""'"""'""""""'""'"""'""'"""""""'""'"""'""""""'""'"""""""'""""""""'"""'""'"""""""'""'"""'"""""'""""""""'""""""'""'""'""'"""'""'"""""""'""'""'""'"""'""""'""""""'""'"""'""""""""'"""""""'""'"""'""""""'""'""'"""'""'"""""""'""'""'"""'"""""'""""""""'""""""'""'"""'""'"""""""'""'"""'""""'""""""'"""'""'"""""""'""""""""'"""'"""""'""""""""'""""""""'""""""'"""'""""""""'""""""""'"""""""'"""'""""""'"""""""'"""'""'"""""""'"""""""'"""'"""""'""'""'""""""'""'""'"""'""""""""'"""""""'""'""'"""'""""'""""'""""'""""""'""'"""'""'""'""'""'""'""""""'"""'""'""'""'""'"""'"""""'""""""""'""""""""'""""""""'""""""""'""""""""'""""""'""""""""'"""'""""""""'""""""""'"""""""'""""""""'"""'"'"""""""""'""""""'""'""""""'"""'""'""'"""""""'"""'""""""""'"""""""'""'"""""""'"""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'"""""""'""'"""'"""""""'"""""""'"""'""""""'""""""""'"""'""'""'"""""""'"""'"'"""""""'""'"""
> 
> 
> 
This was an amazing challenge. Thank you for posting!
## Explanation
To get a feel for what Unreadable can and can’t do, imagine Brainfuck with an infinite tape in both directions, but instead of a memory pointer moving one cell at a time, you can access any memory cell by dereferencing a pointer. This comes in quite handy in this solution, although other arithmetic operations — including modulo — have to be done by hand.
Here is the program as pseudocode with director’s commentary:
```
// Initialize memory pointer. Why 5 will be explained at the very end!
ptr = 5
// FIRST PASS:
// Read all characters from stdin, store them in memory, and also keep track of the
// current line number at each character.
// We need the +1 here so that EOF, which is -1, ends the loop. We increment ptr by 2
// because we use two memory cells for each input character: one contains the actual
// character (which we store here); the other will contain the line number at which the
// character occurs (updated at the end of this loop body).
while ch = (*(ptr += 2) = read) + 1:
    // At this point, ch will be one more than the actual value.
    // However, the most code-economical way for the following loop is to
    // decrement inside the while condition. This way we get one fewer
    // iteration than the value of ch. Thus, the +1 comes in handy.
    // We are now going to calculate modulo 4 and 5. Why? Because
    // the mod 4 and 5 values of the desired input characters are:
    //
    //  ch  %5  %4
    //  ^   1
    //  v   2
    //  k   3
    //  j   4
    //  ▲   0   2
    //  ▼   0   0
    //
    // As you can see, %5 allows us to differentiate all of them except ▲/▼,
    // so we use %4 to differentiate between those two.
    mod4 = 0      // read Update 2 to find out why mod5 = 0 is missing
    while --ch:
        mod5 = mod5 ? mod5 + 1 : -4
        mod4 = mod4 ? mod4 + 1 : -3
    // At the end of this loop, the value of mod5 is ch % 5, except that it
    // uses negative numbers: -4 instead of 1, -3 instead of 2, etc. up to 0.
    // Similarly, mod4 is ch % 4 with negative numbers.
    // How many lines do we need to go up or down?
    // We deliberately store a value 1 higher here, which serves two purposes.
    // One, as already stated, while loops are shorter in code if the decrement
    // happens inside the while condition. Secondly, the number 1 ('""") is
    // much shorter than 0 ('""""""""'""").
    up = (mod5 ? mod5+1 ? mod5+3 ? 1 : 3 : 2 : mod4 ? 3 : 1)
    dn = (mod5 ? mod5+2 ? mod5+4 ? 1 : 3 : 2 : mod4 ? 1 : 3)
    // As an aside, here’s the reason I made the modulos negative. The -1 instruction
    // is much longer than the +1 instruction. In the above while loop, we only have
    // two negative numbers (-3 and -4). If they were positive, then the conditions in
    // the above ternaries, such as mod5+3, would have to be mod5-3 etc. instead. There
    // are many more of those, so the code would be longer.
    // Update the line numbers. The variables updated here are:
    // curLine = current line number (initially 0)
    // minLine = smallest linenum so far, relative to curLine (always non-positive)
    // maxLine = highest linenum so far, relative to curLine (always non-negative)
    // This way, we will know the vertical extent of our foray at the end.
    while --up:
        curLine--
        minLine ? minLine++ : no-op
        maxLine++
    while --dn:
        curLine++
        minLine--
        maxLine ? maxLine-- : no-op
    // Store the current line number in memory, but +1 (for a later while loop)
    *(ptr + 1) = curLine + 1
// At the end of this, minLine and maxLine are still relative to curLine.
// The real minimum line number is curLine + minLine.
// The real maximum line number is curLine + maxLine.
// The total number of lines to output is maxLine - minLine.
// Calculate the number of lines (into maxLine) and the real minimum
// line number (into curLine) in a single loop. Note that maxLine is
// now off by 1 because it started at 0 and thus the very line in which
// everything began was never counted.
while (++minLine) - 1:
  curLine--
  maxLine++
// Make all the row numbers in memory positive by adding curLine to all of them.
while (++curLine) - 1:
  ptr2 = ptr + 1
  while (ptr2 -= 2) - 2:    // Why -2? Read until end!
    *ptr2++
// Finally, output line by line. At each line, we go through the memory, output the
// characters whose the line number is 0, and decrement that line number. This way,
// characters “come into view” in each line by passing across the line number 0.
while (--maxLine) + 2:    // +2 because maxLine is off by 1
  ptr3 = 5
  while (ptr -= 2) - 5:
    print (*((ptr3 += 2) + 1) = *(ptr3 + 1) - 1) ? 32 : *ptr3   // 32 = space
  ptr = ptr3 + 2
  print 10  // newline
```
So much for the program logic. Now we need to **translate this to Unreadable** and use a few more interesting golfing tricks.
Variables are always dereferenced numerically in Unreadable (e.g. `a = 1` becomes something like `*(1) = 1`). Some numerical literals are longer than others; the shortest is 1, followed by 2, etc. To show just how much longer negative numbers are, here are the numbers from -1 to 7:
```
-1  '""""""""'""""""""'"""  22
 0  '""""""""'"""           13
 1  '"""                     4
 2  '""'"""                  7
 3  '""'""'"""              10
 4  '""'""'""'"""           13
 5  '""'""'""'""'"""        16
 6  '""'""'""'""'""'"""     19
 7  '""'""'""'""'""'""'"""  22
```
Clearly, we want to **allocate variable #1 to the one that occurs most frequently** in the code. In the first while loop, this is definitely `mod5`, which comes up 10 times. But we don’t need `mod5` anymore after the first while loop, so we can re-allocate the same memory location to other variables we use later on. These are `ptr2` and `ptr3`. Now the variable is referenced 21 times in total. (If you’re trying to count the number of occurrences yourself, remember to count something like `a++` twice, once for getting the value and once for setting it.)
There’s only one other variable that we can re-use; after we calculated the modulo values, `ch` is no longer needed. `up` and `dn` come up the same number of times, so either is fine. Let’s merge `ch` with `up`.
This leaves a total of 8 unique variables. We could allocate variables 0 to 7 and then start the memory block (containing the characters and line numbers) at 8. But! Since 7 is the same length in code as −1, we could as well use variables −1 to 6 and start the memory block at 7. This way, every reference to the start position of the memory block is slightly shorter in code! This leaves us with the following assignments:
```
-1    dn
 0                      ← ptr or minLine?
 1    mod5, ptr2, ptr3
 2    curLine
 3    maxLine
 4                      ← ptr or minLine?
 5    ch, up
 6    mod4
 7... [data block]
```
Now **this explains the initialization at the very top:** it’s 5 because it’s 7 (the beginning of the memory block) minus 2 (the obligatory increment in the first while condition). The same goes for the other two occurrences of 5 in the last loop.
Note that, since 0 and 4 are the same length in code, `ptr` and `minLine` could be allocated either way around. ... Or could they?
**What about the mysterious 2 in the second-last while loop?** Shouldn’t this be a 6? We only want to decrement the numbers in the data block, right? Once we reach 6, we’re outside the data block and we should stop! It would be a buffer overflow bug error failure security vulnerability!
Well, think about what happens if we don’t stop. We decrement variables 6 and 4. Variable 6 is `mod4`. That’s only used in the first while loop and no longer needed here, so no harm done. What about variable 4? What do you think, should variable 4 be `ptr` or should it be `minLine`? That’s right, `minLine` is no longer used at this point either! Thus, variable #4 is `minLine` and we can safely decrement it and do no damage!
**UPDATE 1!** Golfed from 2199 to 2145 bytes by realizing that `dn` can *also* be merged with `mod5`, even though `mod5` is still used in the calculation of the value for `dn`! New variable assignment is now:
```
 0    ptr
 1    mod5, dn, ptr2, ptr3
 2    curLine
 3    maxLine
 4    minLine
 5    ch, up
 6    mod4
 7... [data block]
```
**UPDATE 2!** Golfed from 2145 to 2134 bytes by realizing that, since `mod5` is now in the same variable as `dn`, which is counted to 0 in a while loop, `mod5` no longer needs to be explicitly initialized to 0.
**UPDATE 3!** Golfed from 2134 to 2104 bytes by realizing two things. First, although the “negative modulo” idea was worth it for `mod5`, the same reasoning does not apply to `mod4` because we never test against `mod4+2` etc. Therefore, changing `mod4 ? mod4+1 : -3` to `mod4 ? mod4-1 : 3` takes us to 2110 bytes. Second, since `mod4` is always 0 or 2, we can initialize `mod4` to 2 instead of 0 and reverse the two ternaries (`mod4 ? 3 : 1` instead of `mod4 ? 1 : 3`).
**UPDATE 4!** Golfed from 2104 to 2087 bytes by realizing that the while loop that calculates the modulo values always runs at least once, and in such a case, Unreadable allows you to re-use the last statement’s value in another expression. Thus, instead of `while --ch: [...]; up = (mod5 ? mod5+1 ? [...]` we now have `up = ((while --ch: [...]) ? mod5+1 ? [...]` (and inside that while loop, we calculate `mod4` first, so that `mod5` is the last statement).
**UPDATE 5!** Golfed from 2087 to 2084 bytes by realizing that instead of writing out the constants `32` and `10` (space and newline), I can store the number 10 in the (now unused) variable #2 (let’s call it `ten`). Instead of `ptr3 = 5` we write `ten = (ptr3 = 5) + 5`, then `32` becomes `ten+22` and `print 10` becomes `print ten`.
[Answer]
# Pyth, 27 bytes
```
jCm.<.[*5lzd\ =+Ztx"v ^k"dz
```
Try it online: [Demonstration](https://pyth.herokuapp.com/?code=jCm.%3C.%5B%2a5lzd%5C+%3D%2BZtx%22v+%5Ek%22dz&input=%5E%5Ek%5Evjk%5E%5Ev&test_suite_input=%5E%5Ek%5Evjk%5E%5Ev%0Akv%5Ev%5Ev%5Ev%5Ev%5Ev%5Ev%5Evk%0A%5E%5E%5E%5E%5E%5E%5Ekkkjjjvvvvvv%0Av%5E%5Evv%5E%5Evvv%5Ev%5Ev%5E%5E%5Evvvv%5E%5Ev%5E%5Evv&debug=0) or [Test Suite](https://pyth.herokuapp.com/?code=jCm.%3C.%5B%2a5lzd%5C+%3D%2BZtx%22v+%5Ek%22dz&input=%5E%5Ek%5Evjk%5E%5Ev&test_suite=1&test_suite_input=%5E%5Ek%5Evjk%5E%5Ev%0Akv%5Ev%5Ev%5Ev%5Ev%5Ev%5Ev%5Evk%0A%5E%5E%5E%5E%5E%5E%5Ekkkjjjvvvvvv%0Av%5E%5Evv%5E%5Evvv%5Ev%5Ev%5E%5E%5Evvvv%5E%5Ev%5E%5Evv&debug=0)
I use `k` and `j` instead of `▲` and `▼`. There are lots of leading and trailing empty lines. You have to search quite a bit to find the image. Here's a **34 byte** version, that removes all leading and trailing empty lines. 
```
j.sCm.<.[*5lzd\ =+Ztx"v ^k"dz]*lzd
```
Try it online: [Demonstration](https://pyth.herokuapp.com/?code=j.sCm.%3C.%5B%2A5lzd%5C%20%3D%2BZtx%22v%20%5Ek%22dz%5D%2Alzd&input=%5E%5Ek%5Evjk%5E%5Ev&test_suite_input=%5E%5Ek%5Evjk%5E%5Ev%0Akv%5Ev%5Ev%5Ev%5Ev%5Ev%5Ev%5Evk%0A%5E%5E%5E%5E%5E%5E%5Ekkkjjjvvvvvv%0Av%5E%5Evv%5E%5Evvv%5Ev%5Ev%5E%5E%5Evvvv%5E%5Ev%5E%5Evv&debug=0) or [Test Suite](https://pyth.herokuapp.com/?code=j.sCm.%3C.%5B%2A5lzd%5C%20%3D%2BZtx%22v%20%5Ek%22dz%5D%2Alzd&input=%5E%5Ek%5Evjk%5E%5Ev&test_suite=1&test_suite_input=%5E%5Ek%5Evjk%5E%5Ev%0Akv%5Ev%5Ev%5Ev%5Ev%5Ev%5Ev%5Evk%0A%5E%5E%5E%5E%5E%5E%5Ekkkjjjvvvvvv%0Av%5E%5Evv%5E%5Evvv%5Ev%5Ev%5E%5E%5Evvvv%5E%5Ev%5E%5Evv&debug=0)
### Explanation:
```
jCm.<.[*5lzd\ =+Ztx"v ^k"dz  implicit: Z = 0
  m                       z  map each char d from input string z to:
                  x"v ^k"d     find d in the string "v ^k", -1 if not found
                 t             -1, that gives -2 for j, -1 for v, 1 for ^ and 2 for k
              =+Z              add this number to Z
     .[*5lzd\                  append spaces on the left and on the right of d, 
                               creating a 5*len(input_string) long string
   .<           Z              rotate this string to the left by Z chars
jC                           transpose and print on lines
```
[Answer]
# CJam, 37 bytes
```
r_,2*:L3*S*f{\_iImd8-g\8>)*L+:L\t}zN*
```
This prints empty lines before and after the desired output, [which has been allowed by the OP](https://codegolf.stackexchange.com/questions/58759/this-question-has-its-ups-and-downs/58765?noredirect=1#comment141570_58759).
Try it online in the [CJam interpreter](http://cjam.aditsu.net/#code=r_%2C2*%3AL3*S*f%7B%5C_iImd8-g%5C8%3E)*L%2B%3AL%5Ct%7DzN*&input=%E2%96%BC%E2%96%BC).
### How it works
```
r_     e# Read a token from STDIN and push a copy.
,2*:L  e# Compute its length, double it and save it in L.
3*S*   e# Push a string of 6L spaces.
f{     e# For each character C in the input, push C and the string of spaces; then
  \    e#   Swap C with the string of spaces.
  _i   e#   Push a copy of C and cast it to integer.
  Imd  e#   Push quotient and remainder of its division by 18.
  8-g  e#   Push the sign((C%18) - 8). Gives -1 for ^ and ▲, 1 for v and ▼.
  \    e#   Swap the result with the quotient.
  8>)  e#   Push ((C/18) > 1) + 1. Gives 2 for ▲ and ▼, 1 for ^ and v.
  *    e#   Multiply both results. This pushes the correct step value.
  L+:L e#   Add the product to L, updating L.
  \t   e#   Replace the space at index L with C.
}      e# We've built the columns of the output.
z      e# Zip; transpose rows with columns.
N*     e# Join the rows, separating by linefeeds.
```
[Answer]
## Python 2, 102
```
s=input()
j=3*len(s)
exec"w='';i=j=j-1\nfor c in s:i-='kv_^j'.find(c)-2;w+=i and' 'or c\nprint w;"*2*j
```
Prints line by line.
Loops through characters in the input and tracks the current height. The height is updated by one of `+2, +1, -1, -2` as computed by `'kv_^j'.find(c)-2`. There's probably a mod chain that's shorter
When the current height equals the line number (which can be negative), we append the current character to the line, and otherwise append a space. Then, we print the line. Actually, it's shorter to start the height at the current line number and subtract the height changes, appending the character when the value hits `0`.
The line numbers encompass a large enough range that a sequence of up-two or down-two will stay within it. Actually, there's a good amount of excess. If we had an upper bound on the input length, it would be shorter to write, say `j=999`.
Surprisingly, `i and' 'or c` was shorter than the usual `[' ',c][i==0]`. Note that `i` can be negative, which cuts out some usual tricks.
[Answer]
# MATLAB, 116
```
function o=u(a)
x=0;y=1;o='';for c=a b=find(c=='j^ vk')-3;y=y+b;if y<1 o=[zeros(1-y,x);o];y=1;end
x=x+1;o(y,x)=c;end
```
It's a start. The `j` and `k` make it a pain in the neck as I can't find a way to mathematically map from `j^vk` to `[-2 -1 1 2]` and with MATLAB not recognising the Unicode (apparently both up and down have a value of 26 in MATLAB. Go figure!), there are a lot of bytes wasted doing the mapping.
By drawing inspiration from @xnors solution, the code can be reduced by another 14 characters by mapping the control character inside the for loop.
There are also many bytes wasted trying to account for if the input string sends the pattern back below the index at which it started (maybe if there was a limit on the string length I could simplify that bit).
And in its readable form:
```
function o=u(a)
%We start in the top left corner.
x=0; %Although the x coordinate is 1 less than it should be as we add one before storing the character
y=1;
o=''; %Start with a blank array
for c=a
    %Map the current character to [-2 -1 1 2] for 'j^vk' respectively.
    b=find(c=='j^ vk')-3;
    y=y+b; %Offset y by our character
    if y<1 %If it goes out of range of the array
        o=[zeros(1-y,x); o]; %Add enough extra lines to the array. This is a bit of a hack as 0 prints as a space in MATLAB.
        y=1; %Reset the y index as we have now rearranged the array
    end
    x=x+1; %Move to the next x coordinate (this is why we start at x=0
    o(y,x)=c; %Store the control character in the x'th position at the correct height.
end
```
[Answer]
# JavaScript (ES6), 140
Test running the snippet below in a EcmaScript 6 compliant browser (tested on Firefox)
.
```
f=s=>[...s].map(c=>{for(t=r[y+=c>'▲'?2:c>'v'?-2:c>'^'?1:-1]||x;y<0;y++)r=[,...r];r[y]=t+x.slice(t.length)+c,x+=' '},y=0,r=[x=''])&&r.join`
`
// Less golfed
f=s=>(
  y=0,
  x='',
  r=[],
  [...s].forEach( c =>
    {
      y += c > '▲' ? 2 : c > 'v' ? -2 : c > '^' ? 1 : -1;
      t = r[y] || x;
      while (y < 0)
      {
        y++;
        r = [,...r]
      }  
      r[y] = t + x.slice(t.length) + c;
      x += ' '
    }
  ),
  r.join`\n`
)  
//Test
;[
  '^^▲^v▼▲^^v'
, '▲v^v^v^v^v^v^v^v▲'
, '^^^^^^^▲▲▲▼▼▼vvvvvv'
, 'v^^vv^^vvv^v^v^^^vvvv^^v^^vv'  
].forEach(t=>document.write(`${t}<pre>${f(t)}</pre>`))
```
```
pre { border:1px solid #777 }
```
[Answer]
## GS2, 34 bytes
This one correctly calculates the output bounds so no excess whitespace is produced. Here's my solution in hex
```
5e 20 76 6a 05 3e 26 ea 30 e0 6d 40 28 26 cf d3
31 e9 d0 4d 42 5e e2 b1 40 2e e8 29 cf d3 5c e9
9a 54
```
A little explanation is in order. On the stack we have user input as an array of ascii codes. The program starts in a string literal because of the `05`. Here we go.
```
  5e 20 76 6a      # ascii for "^ vj"
  05               # finish string literal and push to stack
  3e               # index - find index in array or -1 if not found
  26               # decrement
ea                 # map array using block of 3 instructions (indented)
  30               # add 
e0                 # create a block of 1 instruction
6d                 # scan (create running total array of array using block)
40                 # duplicate top of stack
28                 # get minimum of array
26                 # decrement
cf                 # pop from stack into register D (this is the "highest" the path goes)
  d3               # push onto stack from register D
  31               # subtract
e9                 # map array using block of 2 instructions
d0                 # push onto stack from register A (unitialized, so it contains stdin)
  4d               # itemize - make singleton array (also is single char string)
  42               # swap top two elements in stack
  5e               # rjust - right justify string
e2                 # make block from 3 instructions
b1                 # zipwith - evaluate block using parallel inputs from two arrays
40                 # duplicate top of stack
  2e               # get length of array/string
e8                 # map array using block of 1 instruction
29                 # get maximum of array
cf                 # pop from stack into register D (this is the "lowest" the path goes)
  d3               # push from register D onto stack
  5c               # ljust - left justify string
e9                 # map array using block of two instructions
9a                 # transpose array of arrays
54                 # show-lines - add a newline to end of each element in array
```
## GS2, 24 bytes
I also have a 24 byte solution that doesn't take as much care calculating output size, and ends up with extra whitespace. I prefer the one with the whitespace kept to a minimum though. 
```
5e 20 76 6a 05 3e 26 ea 30 e0 6d d0 08 4d 42 d1
30 5e d1 5c 09 b1 9a 54
```
[Answer]
# [Crayon](https://github.com/ETHproductions/Crayon), 13 bytes (non-competing)
```
O"^ vj"\CynIq
```
[Try it online!](https://tio.run/nexus/crayon#@@@vFKdQ9mjaHqUY58o8z8L////HxT2atikOJAai48oA "Crayon – TIO Nexus") Uses the real arrows because why not.
Non-competing because Crayon is way newer than this challenge.
### How it works
Crayon is a stack-based language designed to be killer at ASCII-art challenges. It is built around the basis of a 2-dimensional output "canvas", and a "crayon", a cursor that travels around this canvas. Anything that is sent to output is drawn on the canvas at the position of the crayon, and in the direction the crayon is facing. By default, the crayon points East (to the right).
```
O"^ v▼"\CynIq   Implicit: input string is on top of the stack
O               For each char I in the input string:
 "^ v▼"          Push this string.
       \         Swap the top two items (so I is on top).
        C        Take the index of I in the string.
                 This returns 3 for ▼, 2 for v, 0 for ^, and -1 for ▲.
         y       Move the crayon by this number of spaces on the Y-axis (south).
          n      Move the crayon one position north.
                 The crayon has now been translated 2 positions south for ▼,
                 1 south for v, 1 north for ^, and 2 north for ▲.
           Iq    Draw I at the crayon. This automatically moves the crayon forward
                 by the length of I, which is 1 in this case.
```
[Answer]
# [Husk](https://github.com/barbuz/Husk), 30 bytes
```
↔T' z:mR' λm+a▼¹)∫m-2m€"v ^▲"¹
```
[Try it online!](https://tio.run/##yygtzv7//1HblBB1hSqr3CB1hXO7c7UTH03bc2in5qOO1bm6RrmPmtYolSnEPZq2SenQzv///5fFxZWBcVkcCIJZID6IAQA "Husk – Try It Online") or [Verify all tests](https://tio.run/##yygtzv6fm39o26Omxv@P2qaEqCtUWeUGqSuc252rnfho2p5DOzUfdazO1TXKfdS0RqlMIe7RtE1Kh3b@//8/WikOxIkrA6oC0XFlSjpKQEZZHAoEqdcBKgUDIAeG9kBQGRgAVZQBDQBjiD4wC8QHMZRiAQ "Husk – Try It Online")
Uses a few ideas from the Pyth answer, and the lambda can be shortened.
Since `▲` and `▼` are in Husk's codepage, you can see the testcases in their full glory!
## Explanation
```
↔T' z:mR' λm+a▼¹)∫m-2m€"v ^▲"¹
                     m€"v ^▲"  get the indices of each char in the string
                  m-2          subtract 2 from each
                 ∫             cumulative sum
          λm+a▼¹)              add abs(minimum) to each
      mR'                      map each n to n spaces
    z:                         join the spaces with the input
 T'                            Transpose. padding with spaces
↔                              reverse
```
[Answer]
# [pb](https://esolangs.org/wiki/pb) - 136 bytes
```
^w[B!0]{>}v[3*X]<[X]<b[1]^[Y]^>w[B!0]{t[B]<vw[B=0]{v}>w[T=107]{^^b[T]t[0]}w[T=94]{^b[T]t[0]}w[T=118]{vb[T]t[0]}w[T!0]{vvb[T]t[0]}^[Y]^>}
```
Uses `k` and `j` instead of `▲` and `▼`.
A couple of notes:
* `Escape sequences that move the cursor such as \e[B are not allowed. You must produce the output using spaces and newlines.` I do follow this rule! pb uses the concept of a "brush" to output characters. The brush moves around the "canvas" and can print a character immediately below it. However, the actual implementation prints the character using spaces and newlines.
* I wasn't going to bother with this challenge even though I thought it would be fun with pb until I saw the ruling that `You are allowed trailing spaces and/or empty lines`. This is for a couple of reasons:
	+ pb can't *not* have trailing spaces. It always produces rectangular output, padding with spaces if necessary.
	+ This program produces a lot of empty lines. It doesn't know how tall the output is going to be when it starts making it, so for an input of length `n` it starts at `Y=3n+1`. The `-1` is because it's going down `3n` from `Y=-1`, and starting at `Y=2n-1` fails for an input of all `k`.
[You can watch this program in action on YouTube!](https://www.youtube.com/watch?v=8k1M4yX4BUE) This version is slightly modified in that it only goes down to `n-1`. It works for this input, but will fail for others. It does, however, capture a lot nicer.
With comments:
```
^w[B!0]{>}             # Go to the end of the input
v[3*X]                 # Go down 3 times the current X value
<[X]<                  # Go to X=-1 (off screen, won't be printed)
b[1]                   # Leave a non-zero value to find later
^[Y]^>                 # Back to the beginning of the input
w[B!0]{                # For every byte of input:
    t[B]                 # Copy it to T
    <vw[B=0]{v}>         # Go 1 to the right of the character to the left
                         # (either the last one printed or the value at X=-1)
                         # Move the correct amount for each character and print it:
    w[T=107]{^^b[T]t[0]} # k
    w[T=94]{^b[T]t[0]}   # ^
    w[T=118]{vb[T]t[0]}  # v
    w[T!0]{vvb[T]t[0]}   # j (Every other possibility sets T to 0, so if T is not 0
                         #    it must be j. T!0 is shorter than T=106)
    ^[Y]^>               # To the next byte of input to restart the loop
}
```
[Answer]
# Ceylon, 447 bytes
```
import ceylon.language{o=null,v=variable,s=shared}s void y(){v L c;v L f;v L l;v Integer i=0;class L(v L?p,v L?n){s v String t="";s L u=>p else(f=p=L(o,this));s L d=>n else(l=n=L(this,o));s void a(Character c)=>t=t+" ".repeat(i-t.size)+c.string;}f=l=c=L(o,o);for(x in process.readLine()else""){switch(x)case('^'){c=c.u;}case('v'){c=c.d;}case('▲'|'k'){c=c.u.u;}case('▼'|'j'){c=c.d.d;}else{}c.a(x);i++;}print(f.t);while(f!=l){f=f.d;print(f.t);}}
```
Or with line breaks for "readability":
`import ceylon.language{o=null,v=variable,s=shared}s void y(){v L c;v L f;v L l;v Integer i=0;class L(v L?p,v L?n){s v String t="";s L u=>p else(f=p=L(o,this));s L d=>n else(l=n=L(this,o));s void a(Character c)=>t=t+" ".repeat(i-t.size)+c.string;}f=l=c=L(o,o);for(x in process.readLine()else""){switch(x)case('^'){c=c.u;}case('v'){c=c.d;}case('▲'|'k'){c=c.u.u;}case('▼'|'j'){c=c.d.d;}else{}c.a(x);i++;}print(f.t);while(f!=l){f=f.d;print(f.t);}}`
This works with both the ▲/▼ and the j/k input (If we had to support just one of them, the program would be 8 bytes shorter). The last output line is empty when the starting position was on it (i.e. the first input was a `▲` or `^` and we never got below that again later).
Input which is not one of the specified characters will simply be printed as-is, without switching the line:
```
v^^vv^^vvv^v^v^^^Hellovvvv^^v^^vv
```
→
```
  ^   ^         ^Hello
 ^ v ^ v       ^      v       ^
v   v   v ^ ^ ^        v   ^ ^ v
         v v v          v ^ v   v
                         v
```
Here is a formatted version (753 bytes):
```
shared void y() {
    variable L c;
    variable L f;
    variable L l;
    variable Integer i = 0;
    class L(variable L? p, variable L? n) {
        shared variable String t = "";
        shared L u => p else (f = p = L(null, this));
        shared L d => n else (l = n = L(this, null));
        shared void a(Character c) => t = t + " ".repeat(i - t.size) + c.string;
    }
    f = l = c = L(null, null);
    for (x in process.readLine() else "") {
        switch (x)
        case ('^') { c = c.u; }
        case ('v') { c = c.d; }
        case ('▲' | 'k') { c = c.u.u; }
        case ('▼' | 'j') { c = c.d.d; }
        else {}
        c.a(x);
        i++;
    }
    print(f.t);
    while (f != l) {
        f = f.d;
        print(f.t);
    }
}
```
This is an almost straight-forward "object-oriented" program ... the (local) class `L` (line buffer) stores a line of text (in `t`), as well as (nullable) pointers to the next (`n`) and previous (`p`) line. The (not nullable) attributes `u` (for up) and `d` (for down) initialize those if needed (with a reverse pointer to itself), and in this case also keep track of the first and last line overall (in the `f` and `l` variables).
The `a` (append) method appends a character to this line, including some spaces eventually necessary.
`c` is the current line. We parse the input string (using `readLine` as the input should be on one line) using a switch statement which updates the current line, and then calls the append method.
After parsing is done, we iterate over the lines from the first to the last, printing each of them. (This destroys the `f` pointer, if it were needed afterwards, we should have used a separate variable for this.)
Some used tricks for golfing:
* Some stuff which in other languages would be keywords are actually just identifiers in the `ceylon.language` package, and can be renamed with an alias import – we used this for the annotations `shared` (used 5×) and `variable` (used 6×), as well as for the object `null` (used 4×):
```
import ceylon.language{o=null,v=variable,s=shared}
```
(Trivia: The Formatter in the Ceylon IDE formats some built-in language annotations, between them `variable` and `shared`, by putting them in the same line as the annotated declaration, contrasted to custom annotations, which are put on a separate line above the declaration. This makes the formatted version of the golfed program unreadable, therefore I changed the alias-imports back for this version.)
`this`, `void`, `case`, `else` are actual keywords and cannot be renamed this way, and `Integer`, `String` and `Character` appear just once each, so there is nothing to be gained by importing.
* Originally I also had a separate ScreenBuffer class (which kept track of the linked list of line buffers, the current index, and so on), but as there was only ever one object of it, it was optimized away.
* That Screenbuffer class also had `up` and `down` methods, which were called from the parser (and just did `currentLine = currentLine.up` respectively `currentLine = currentLine.down`). It showed that directly doing this in the parser's switch is shorter. It also allowed to write `currentLine = currentLine.up.up` (which later became `c = c.u.u`) instead of `currentLine = currentLine.up;currentLine = currentLine.up`.
* Originally we did pass the current index as an argument into the append method (and even to the parser from the loop) – having it a variable in the containing function is shorter.
* Originally my printAll method used the current pointer and moved it first up until the current line was empty, and then down while printing each line. This broke when using ▲ and ▼ to jump over lines, so we had to explicitly append something in those jumped lines instead. Keeping track of first/last line proved easier (though it made it necessary to use two print statements, because there is no do-while-loop in Ceylon).
* Originally I had something like this:
```
  String? input = process.readLine();
  if(exists input) {
     for(x in input) {
         ...
     }
  }
```
[`process.readLine`](https://modules.ceylon-lang.org/repo/1/ceylon/language/1.1.0/module-doc/api/process.object.html#readLine) returns `null` if there is no line which can be read (because the input has been closed), and the Ceylon compiler requires me to checkfor that before I access `input`. As in this case I want to do nothing, I can equivalently use the `else` operator which returns its first argument if not null, and otherwise its second argument, saving the variable and the if-statement. (This also would allow us to encode a default input for testing: `for (x in process.readLine() else "^^▲^v▼▲^^v") {`)
[Answer]
# JavaScript (ES6), 228 bytes
```
E=(r,p=(' '[M='repeat'](Z=r.length)+',')[M](Z*4),i=Z*2,k=0)=>Z>k?E(r,(p.split(',').map((o,q)=>q==i?o.slice(0,k)+r[k]+o.slice(k++):o)).join`,`,i+(H={'^':-1,k:-2,j:2,v:1})[r[k]],k):p.split(',').join`
`.replace(/\s+\n$|^\s+\n/g,'')
```
Well, here is a (rather long) recursive solution that passes all of the test cases given. It was a nice challenge. This uses `k` and `j` in place of `▼` and `▲`.
## Test Snippet
Although the submission itself can only handle `k,j`, the following snippet can handle both `k,j` and `▼,▲`.
```
E=(r,p=(' '[M='repeat'](Z=r.length)+',')[M](Z*4),i=Z*2,k=0)=>Z>k?E(r,(p.split(',').map((o,q)=>q==i?o.slice(0,k)+r[k]+o.slice(k++):o)).join`,`,i+(H={'^':-1,k:-2,j:2,v:1})[r[k]],k):p.split(',').join`
`.replace(/\s+\n$|^\s+\n/g,'')
```
```
Input: <input type="text" oninput=o.textContent=E(this.value.replace(/▲/g,'k').replace(/▼/g,'j'))></input>
<pre id='o'></pre>
```
]